summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore3
-rw-r--r--AUTHORS2
-rw-r--r--ChangeLog101
-rw-r--r--Makefile139
-rw-r--r--Makefile-gyp319
-rw-r--r--common.gypi43
-rwxr-xr-xconfigure288
-rwxr-xr-xconfigure-gyp247
-rw-r--r--deps/openssl/config/piii/openssl/opensslconf-posix.h14
-rw-r--r--deps/openssl/openssl.gyp2
-rw-r--r--deps/uv/.mailmap1
-rw-r--r--deps/uv/AUTHORS6
-rw-r--r--deps/uv/config-mingw.mk2
-rw-r--r--deps/uv/config-unix.mk3
-rw-r--r--deps/uv/include/uv-private/eio.h119
-rw-r--r--deps/uv/include/uv-private/ngx-queue.h4
-rw-r--r--deps/uv/include/uv-private/uv-unix.h10
-rw-r--r--deps/uv/include/uv-private/uv-win.h35
-rw-r--r--deps/uv/include/uv.h120
-rw-r--r--deps/uv/src/unix/core.c125
-rw-r--r--deps/uv/src/unix/darwin.c235
-rw-r--r--deps/uv/src/unix/eio/config_linux.h7
-rw-r--r--deps/uv/src/unix/eio/eio.c177
-rw-r--r--deps/uv/src/unix/error.c4
-rw-r--r--deps/uv/src/unix/freebsd.c181
-rw-r--r--deps/uv/src/unix/fs.c20
-rw-r--r--deps/uv/src/unix/internal.h46
-rw-r--r--deps/uv/src/unix/kqueue.c5
-rw-r--r--deps/uv/src/unix/linux.c370
-rw-r--r--deps/uv/src/unix/openbsd.c164
-rw-r--r--deps/uv/src/unix/pipe.c4
-rw-r--r--deps/uv/src/unix/process.c7
-rw-r--r--deps/uv/src/unix/sunos.c269
-rw-r--r--deps/uv/src/unix/tcp.c1
-rw-r--r--deps/uv/src/unix/thread.c154
-rw-r--r--deps/uv/src/unix/udp.c128
-rw-r--r--deps/uv/src/unix/uv-eio.c40
-rw-r--r--deps/uv/src/uv-common.c84
-rw-r--r--deps/uv/src/uv-common.h2
-rw-r--r--deps/uv/src/win/core.c44
-rw-r--r--deps/uv/src/win/fs-event.c2
-rw-r--r--deps/uv/src/win/internal.h18
-rw-r--r--deps/uv/src/win/process.c10
-rw-r--r--deps/uv/src/win/tcp.c12
-rw-r--r--deps/uv/src/win/thread.c332
-rw-r--r--deps/uv/src/win/threads.c81
-rw-r--r--deps/uv/src/win/tty.c8
-rw-r--r--deps/uv/src/win/udp.c10
-rw-r--r--deps/uv/src/win/util.c357
-rw-r--r--deps/uv/src/win/winapi.c28
-rw-r--r--deps/uv/src/win/winapi.h28
-rw-r--r--deps/uv/src/win/winsock.h24
-rw-r--r--deps/uv/test/benchmark-list.h2
-rw-r--r--deps/uv/test/benchmark-tcp-write-batch.c4
-rw-r--r--deps/uv/test/benchmark-thread.c64
-rw-r--r--deps/uv/test/benchmark-udp-packet-storm.c2
-rw-r--r--deps/uv/test/blackhole-server.c4
-rw-r--r--deps/uv/test/run-tests.c10
-rw-r--r--deps/uv/test/runner.c6
-rw-r--r--deps/uv/test/runner.h10
-rw-r--r--deps/uv/test/task.h9
-rw-r--r--deps/uv/test/test-eio-overflow.c90
-rw-r--r--deps/uv/test/test-fs-event.c22
-rw-r--r--deps/uv/test/test-list.h23
-rw-r--r--deps/uv/test/test-mutexes.c63
-rw-r--r--deps/uv/test/test-platform-output.c83
-rw-r--r--deps/uv/test/test-process-title.c42
-rw-r--r--deps/uv/test/test-run-once.c44
-rw-r--r--deps/uv/test/test-spawn.c31
-rw-r--r--deps/uv/test/test-thread.c183
-rw-r--r--deps/uv/test/test-timer.c8
-rw-r--r--deps/uv/test/test-util.c97
-rw-r--r--deps/uv/uv.gyp19
-rw-r--r--deps/v8/AUTHORS2
-rw-r--r--deps/v8/ChangeLog428
-rw-r--r--deps/v8/LICENSE6
-rw-r--r--deps/v8/Makefile105
-rw-r--r--deps/v8/SConstruct39
-rw-r--r--deps/v8/benchmarks/spinning-balls/index.html11
-rw-r--r--deps/v8/benchmarks/spinning-balls/splay-tree.js326
-rw-r--r--deps/v8/benchmarks/spinning-balls/v.js498
-rw-r--r--deps/v8/build/android.gypi225
-rw-r--r--deps/v8/build/common.gypi124
-rwxr-xr-xdeps/v8/build/gyp_v85
-rw-r--r--deps/v8/build/mipsu.gypi34
-rw-r--r--deps/v8/build/standalone.gypi45
-rwxr-xr-x[-rw-r--r--]deps/v8/include/v8-debug.h5
-rw-r--r--deps/v8/include/v8-profiler.h20
-rw-r--r--deps/v8/include/v8.h117
-rw-r--r--deps/v8/include/v8stdint.h3
-rw-r--r--deps/v8/preparser/preparser-process.cc35
-rwxr-xr-x[-rw-r--r--]deps/v8/src/SConscript7
-rw-r--r--deps/v8/src/accessors.cc51
-rw-r--r--deps/v8/src/accessors.h3
-rw-r--r--deps/v8/src/allocation.cc9
-rw-r--r--deps/v8/src/allocation.h7
-rw-r--r--deps/v8/src/api.cc340
-rw-r--r--deps/v8/src/api.h28
-rw-r--r--deps/v8/src/arm/assembler-arm-inl.h42
-rw-r--r--deps/v8/src/arm/assembler-arm.cc18
-rw-r--r--deps/v8/src/arm/assembler-arm.h22
-rw-r--r--deps/v8/src/arm/builtins-arm.cc1290
-rw-r--r--deps/v8/src/arm/code-stubs-arm.cc1926
-rw-r--r--deps/v8/src/arm/code-stubs-arm.h257
-rw-r--r--deps/v8/src/arm/codegen-arm.cc351
-rw-r--r--deps/v8/src/arm/codegen-arm.h26
-rw-r--r--deps/v8/src/arm/constants-arm.h57
-rw-r--r--deps/v8/src/arm/cpu-arm.cc2
-rw-r--r--deps/v8/src/arm/debug-arm.cc128
-rw-r--r--deps/v8/src/arm/deoptimizer-arm.cc162
-rw-r--r--deps/v8/src/arm/disasm-arm.cc36
-rw-r--r--deps/v8/src/arm/frames-arm.h50
-rw-r--r--deps/v8/src/arm/full-codegen-arm.cc851
-rw-r--r--deps/v8/src/arm/ic-arm.cc375
-rw-r--r--deps/v8/src/arm/lithium-arm.cc290
-rw-r--r--deps/v8/src/arm/lithium-arm.h140
-rw-r--r--deps/v8/src/arm/lithium-codegen-arm.cc1005
-rw-r--r--deps/v8/src/arm/lithium-codegen-arm.h47
-rw-r--r--deps/v8/src/arm/lithium-gap-resolver-arm.cc17
-rw-r--r--deps/v8/src/arm/macro-assembler-arm.cc1041
-rw-r--r--deps/v8/src/arm/macro-assembler-arm.h405
-rw-r--r--deps/v8/src/arm/regexp-macro-assembler-arm.cc26
-rw-r--r--deps/v8/src/arm/simulator-arm.cc37
-rw-r--r--deps/v8/src/arm/simulator-arm.h7
-rw-r--r--deps/v8/src/arm/stub-cache-arm.cc1831
-rw-r--r--deps/v8/src/array.js188
-rw-r--r--deps/v8/src/assembler.cc148
-rw-r--r--deps/v8/src/assembler.h101
-rw-r--r--deps/v8/src/ast-inl.h121
-rw-r--r--deps/v8/src/ast.cc636
-rw-r--r--deps/v8/src/ast.h1526
-rw-r--r--deps/v8/src/atomicops_internals_mips_gcc.h48
-rw-r--r--deps/v8/src/atomicops_internals_x86_macosx.h44
-rw-r--r--deps/v8/src/bignum-dtoa.h2
-rw-r--r--deps/v8/src/bootstrapper.cc495
-rw-r--r--deps/v8/src/bootstrapper.h2
-rw-r--r--deps/v8/src/builtins.cc417
-rw-r--r--deps/v8/src/builtins.h60
-rw-r--r--deps/v8/src/bytecodes-irregexp.h8
-rw-r--r--deps/v8/src/cached-powers.cc12
-rw-r--r--deps/v8/src/char-predicates-inl.h4
-rw-r--r--deps/v8/src/checks.h141
-rw-r--r--deps/v8/src/code-stubs.cc176
-rw-r--r--deps/v8/src/code-stubs.h284
-rw-r--r--deps/v8/src/codegen.cc15
-rw-r--r--deps/v8/src/codegen.h15
-rw-r--r--deps/v8/src/collection.js249
-rw-r--r--deps/v8/src/compilation-cache.cc45
-rw-r--r--deps/v8/src/compilation-cache.h34
-rw-r--r--deps/v8/src/compiler-intrinsics.h77
-rw-r--r--deps/v8/src/compiler.cc152
-rw-r--r--deps/v8/src/compiler.h64
-rw-r--r--deps/v8/src/contexts.cc206
-rw-r--r--deps/v8/src/contexts.h129
-rw-r--r--deps/v8/src/conversions-inl.h48
-rw-r--r--deps/v8/src/conversions.h20
-rw-r--r--deps/v8/src/cpu-profiler.cc13
-rw-r--r--deps/v8/src/cpu-profiler.h6
-rw-r--r--deps/v8/src/cpu.h2
-rw-r--r--deps/v8/src/d8-debug.cc11
-rw-r--r--deps/v8/src/d8-posix.cc3
-rw-r--r--deps/v8/src/d8-readline.cc37
-rw-r--r--deps/v8/src/d8.cc365
-rw-r--r--deps/v8/src/d8.gyp6
-rw-r--r--deps/v8/src/d8.h44
-rw-r--r--deps/v8/src/d8.js109
-rw-r--r--deps/v8/src/date.js51
-rw-r--r--deps/v8/src/debug-agent.cc2
-rw-r--r--deps/v8/src/debug-agent.h1
-rw-r--r--deps/v8/src/debug-debugger.js149
-rw-r--r--deps/v8/src/debug.cc565
-rw-r--r--deps/v8/src/debug.h105
-rw-r--r--deps/v8/src/deoptimizer.cc413
-rw-r--r--deps/v8/src/deoptimizer.h111
-rw-r--r--deps/v8/src/disassembler.cc2
-rw-r--r--deps/v8/src/double.h6
-rw-r--r--deps/v8/src/dtoa.h6
-rw-r--r--deps/v8/src/elements.cc360
-rw-r--r--deps/v8/src/elements.h18
-rw-r--r--deps/v8/src/execution.cc183
-rw-r--r--deps/v8/src/execution.h15
-rw-r--r--deps/v8/src/extensions/experimental/break-iterator.cc252
-rw-r--r--deps/v8/src/extensions/experimental/break-iterator.h89
-rw-r--r--deps/v8/src/extensions/experimental/collator.cc222
-rw-r--r--deps/v8/src/extensions/experimental/collator.h68
-rw-r--r--deps/v8/src/extensions/experimental/datetime-format.cc384
-rw-r--r--deps/v8/src/extensions/experimental/datetime-format.h83
-rw-r--r--deps/v8/src/extensions/experimental/experimental.gyp105
-rw-r--r--deps/v8/src/extensions/experimental/i18n-extension.cc74
-rw-r--r--deps/v8/src/extensions/experimental/i18n-extension.h54
-rw-r--r--deps/v8/src/extensions/experimental/i18n-js2c.py126
-rw-r--r--deps/v8/src/extensions/experimental/i18n-locale.cc111
-rw-r--r--deps/v8/src/extensions/experimental/i18n-locale.h60
-rw-r--r--deps/v8/src/extensions/experimental/i18n-natives.h43
-rw-r--r--deps/v8/src/extensions/experimental/i18n-utils.cc87
-rw-r--r--deps/v8/src/extensions/experimental/i18n-utils.h69
-rw-r--r--deps/v8/src/extensions/experimental/i18n.js380
-rw-r--r--deps/v8/src/extensions/experimental/language-matcher.cc252
-rw-r--r--deps/v8/src/extensions/experimental/language-matcher.h95
-rw-r--r--deps/v8/src/extensions/experimental/number-format.cc374
-rw-r--r--deps/v8/src/extensions/experimental/number-format.h71
-rw-r--r--deps/v8/src/extensions/gc-extension.cc12
-rw-r--r--deps/v8/src/factory.cc250
-rw-r--r--deps/v8/src/factory.h72
-rw-r--r--deps/v8/src/fast-dtoa.h4
-rw-r--r--deps/v8/src/flag-definitions.h135
-rw-r--r--deps/v8/src/flags.cc5
-rw-r--r--deps/v8/src/flags.h3
-rw-r--r--deps/v8/src/frames-inl.h90
-rw-r--r--deps/v8/src/frames.cc245
-rw-r--r--deps/v8/src/frames.h103
-rw-r--r--deps/v8/src/full-codegen.cc398
-rw-r--r--deps/v8/src/full-codegen.h178
-rw-r--r--deps/v8/src/gdb-jit.cc50
-rw-r--r--deps/v8/src/global-handles.cc2
-rw-r--r--deps/v8/src/globals.h42
-rw-r--r--deps/v8/src/handles.cc294
-rw-r--r--deps/v8/src/handles.h111
-rw-r--r--deps/v8/src/hashmap.cc8
-rw-r--r--deps/v8/src/hashmap.h9
-rw-r--r--deps/v8/src/heap-inl.h248
-rw-r--r--deps/v8/src/heap-profiler.cc3
-rw-r--r--deps/v8/src/heap-profiler.h2
-rw-r--r--deps/v8/src/heap.cc2526
-rw-r--r--deps/v8/src/heap.h904
-rw-r--r--deps/v8/src/hydrogen-instructions.cc452
-rw-r--r--deps/v8/src/hydrogen-instructions.h981
-rw-r--r--deps/v8/src/hydrogen.cc1765
-rw-r--r--deps/v8/src/hydrogen.h122
-rw-r--r--deps/v8/src/ia32/assembler-ia32-inl.h47
-rw-r--r--deps/v8/src/ia32/assembler-ia32.cc258
-rw-r--r--deps/v8/src/ia32/assembler-ia32.h102
-rw-r--r--deps/v8/src/ia32/builtins-ia32.cc1165
-rw-r--r--deps/v8/src/ia32/code-stubs-ia32.cc2456
-rw-r--r--deps/v8/src/ia32/code-stubs-ia32.h301
-rw-r--r--deps/v8/src/ia32/codegen-ia32.cc411
-rw-r--r--deps/v8/src/ia32/codegen-ia32.h16
-rw-r--r--deps/v8/src/ia32/cpu-ia32.cc2
-rw-r--r--deps/v8/src/ia32/debug-ia32.cc151
-rw-r--r--deps/v8/src/ia32/deoptimizer-ia32.cc235
-rw-r--r--deps/v8/src/ia32/disasm-ia32.cc88
-rw-r--r--deps/v8/src/ia32/frames-ia32.h28
-rw-r--r--deps/v8/src/ia32/full-codegen-ia32.cc1088
-rw-r--r--deps/v8/src/ia32/ic-ia32.cc419
-rw-r--r--deps/v8/src/ia32/lithium-codegen-ia32.cc1102
-rw-r--r--deps/v8/src/ia32/lithium-codegen-ia32.h66
-rw-r--r--deps/v8/src/ia32/lithium-gap-resolver-ia32.cc18
-rw-r--r--deps/v8/src/ia32/lithium-ia32.cc699
-rw-r--r--deps/v8/src/ia32/lithium-ia32.h185
-rw-r--r--deps/v8/src/ia32/macro-assembler-ia32.cc1261
-rw-r--r--deps/v8/src/ia32/macro-assembler-ia32.h359
-rw-r--r--deps/v8/src/ia32/regexp-macro-assembler-ia32.cc123
-rw-r--r--deps/v8/src/ia32/stub-cache-ia32.cc1793
-rw-r--r--deps/v8/src/ic-inl.h9
-rw-r--r--deps/v8/src/ic.cc1308
-rw-r--r--deps/v8/src/ic.h237
-rw-r--r--deps/v8/src/incremental-marking-inl.h133
-rw-r--r--deps/v8/src/incremental-marking.cc926
-rw-r--r--deps/v8/src/incremental-marking.h278
-rw-r--r--deps/v8/src/inspector.cc4
-rw-r--r--deps/v8/src/inspector.h8
-rw-r--r--deps/v8/src/interpreter-irregexp.cc37
-rw-r--r--deps/v8/src/interpreter-irregexp.h12
-rw-r--r--deps/v8/src/isolate-inl.h13
-rw-r--r--deps/v8/src/isolate.cc226
-rw-r--r--deps/v8/src/isolate.h117
-rw-r--r--deps/v8/src/json-parser.h11
-rw-r--r--deps/v8/src/json.js2
-rw-r--r--deps/v8/src/jsregexp.cc27
-rw-r--r--deps/v8/src/jsregexp.h13
-rw-r--r--deps/v8/src/list-inl.h9
-rw-r--r--deps/v8/src/list.h6
-rw-r--r--deps/v8/src/lithium-allocator.cc136
-rw-r--r--deps/v8/src/lithium-allocator.h32
-rw-r--r--deps/v8/src/lithium.cc32
-rw-r--r--deps/v8/src/lithium.h66
-rw-r--r--deps/v8/src/liveedit-debugger.js49
-rw-r--r--deps/v8/src/liveedit.cc54
-rw-r--r--deps/v8/src/liveobjectlist-inl.h4
-rw-r--r--deps/v8/src/liveobjectlist.cc70
-rw-r--r--deps/v8/src/liveobjectlist.h10
-rw-r--r--deps/v8/src/log.cc25
-rw-r--r--deps/v8/src/log.h7
-rw-r--r--deps/v8/src/macro-assembler.h73
-rw-r--r--deps/v8/src/macros.py12
-rw-r--r--deps/v8/src/mark-compact-inl.h119
-rw-r--r--deps/v8/src/mark-compact.cc4240
-rw-r--r--deps/v8/src/mark-compact.h677
-rw-r--r--deps/v8/src/math.js38
-rw-r--r--deps/v8/src/messages.cc16
-rw-r--r--deps/v8/src/messages.js175
-rw-r--r--deps/v8/src/mips/assembler-mips-inl.h58
-rw-r--r--deps/v8/src/mips/assembler-mips.cc22
-rw-r--r--deps/v8/src/mips/assembler-mips.h21
-rw-r--r--deps/v8/src/mips/builtins-mips.cc1412
-rw-r--r--deps/v8/src/mips/code-stubs-mips.cc2556
-rw-r--r--deps/v8/src/mips/code-stubs-mips.h322
-rw-r--r--deps/v8/src/mips/codegen-mips.cc365
-rw-r--r--deps/v8/src/mips/codegen-mips.h29
-rw-r--r--deps/v8/src/mips/constants-mips.cc4
-rw-r--r--deps/v8/src/mips/constants-mips.h176
-rw-r--r--deps/v8/src/mips/cpu-mips.cc14
-rw-r--r--deps/v8/src/mips/debug-mips.cc129
-rw-r--r--deps/v8/src/mips/deoptimizer-mips.cc811
-rw-r--r--deps/v8/src/mips/frames-mips.h55
-rw-r--r--deps/v8/src/mips/full-codegen-mips.cc847
-rw-r--r--deps/v8/src/mips/ic-mips.cc405
-rw-r--r--deps/v8/src/mips/lithium-codegen-mips.cc4745
-rw-r--r--deps/v8/src/mips/lithium-codegen-mips.h406
-rw-r--r--deps/v8/src/mips/lithium-gap-resolver-mips.cc318
-rw-r--r--deps/v8/src/mips/lithium-gap-resolver-mips.h83
-rw-r--r--deps/v8/src/mips/lithium-mips.cc2272
-rw-r--r--deps/v8/src/mips/lithium-mips.h2277
-rw-r--r--deps/v8/src/mips/macro-assembler-mips.cc1662
-rw-r--r--deps/v8/src/mips/macro-assembler-mips.h561
-rw-r--r--deps/v8/src/mips/regexp-macro-assembler-mips.cc31
-rw-r--r--deps/v8/src/mips/simulator-mips.cc40
-rw-r--r--deps/v8/src/mips/simulator-mips.h7
-rw-r--r--deps/v8/src/mips/stub-cache-mips.cc1816
-rw-r--r--deps/v8/src/mirror-debugger.js211
-rw-r--r--deps/v8/src/mksnapshot.cc4
-rw-r--r--deps/v8/src/objects-debug.cc62
-rw-r--r--deps/v8/src/objects-inl.h1412
-rw-r--r--deps/v8/src/objects-printer.cc102
-rw-r--r--deps/v8/src/objects-visiting-inl.h155
-rw-r--r--deps/v8/src/objects-visiting.cc15
-rw-r--r--deps/v8/src/objects-visiting.h138
-rw-r--r--deps/v8/src/objects.cc3665
-rw-r--r--deps/v8/src/objects.h1676
-rw-r--r--deps/v8/src/parser.cc1593
-rw-r--r--deps/v8/src/parser.h228
-rw-r--r--deps/v8/src/platform-cygwin.cc9
-rw-r--r--deps/v8/src/platform-freebsd.cc130
-rw-r--r--deps/v8/src/platform-linux.cc235
-rw-r--r--deps/v8/src/platform-macos.cc161
-rw-r--r--deps/v8/src/platform-nullos.cc2
-rw-r--r--deps/v8/src/platform-openbsd.cc452
-rw-r--r--deps/v8/src/platform-posix.cc46
-rw-r--r--deps/v8/src/platform-solaris.cc137
-rw-r--r--deps/v8/src/platform-win32.cc122
-rw-r--r--deps/v8/src/platform.h90
-rw-r--r--deps/v8/src/preparse-data.h6
-rw-r--r--deps/v8/src/preparser-api.cc4
-rw-r--r--deps/v8/src/preparser.cc254
-rw-r--r--deps/v8/src/preparser.h105
-rw-r--r--deps/v8/src/prettyprinter.cc481
-rw-r--r--deps/v8/src/prettyprinter.h103
-rw-r--r--deps/v8/src/profile-generator-inl.h20
-rw-r--r--deps/v8/src/profile-generator.cc632
-rw-r--r--deps/v8/src/profile-generator.h69
-rw-r--r--deps/v8/src/property-details.h152
-rw-r--r--deps/v8/src/property.cc38
-rw-r--r--deps/v8/src/property.h60
-rw-r--r--deps/v8/src/proxy.js51
-rw-r--r--deps/v8/src/regexp-macro-assembler-tracer.cc4
-rw-r--r--deps/v8/src/regexp-macro-assembler.cc6
-rw-r--r--deps/v8/src/regexp.js87
-rw-r--r--deps/v8/src/rewriter.cc50
-rw-r--r--deps/v8/src/runtime-profiler.cc137
-rw-r--r--deps/v8/src/runtime-profiler.h23
-rw-r--r--deps/v8/src/runtime.cc3110
-rw-r--r--deps/v8/src/runtime.h65
-rw-r--r--deps/v8/src/runtime.js36
-rwxr-xr-x[-rw-r--r--]deps/v8/src/scanner.cc111
-rw-r--r--deps/v8/src/scanner.h282
-rw-r--r--deps/v8/src/scopeinfo.cc689
-rw-r--r--deps/v8/src/scopeinfo.h160
-rw-r--r--deps/v8/src/scopes.cc739
-rw-r--r--deps/v8/src/scopes.h254
-rw-r--r--deps/v8/src/serialize.cc378
-rw-r--r--deps/v8/src/serialize.h91
-rw-r--r--deps/v8/src/spaces-inl.h512
-rw-r--r--deps/v8/src/spaces.cc3123
-rw-r--r--deps/v8/src/spaces.h2633
-rw-r--r--deps/v8/src/splay-tree-inl.h6
-rw-r--r--deps/v8/src/store-buffer-inl.h79
-rw-r--r--deps/v8/src/store-buffer.cc719
-rw-r--r--deps/v8/src/store-buffer.h255
-rw-r--r--deps/v8/src/string-search.h20
-rw-r--r--deps/v8/src/string-stream.cc37
-rw-r--r--deps/v8/src/string.js56
-rw-r--r--deps/v8/src/strtod.cc1
-rw-r--r--deps/v8/src/stub-cache.cc1670
-rw-r--r--deps/v8/src/stub-cache.h744
-rw-r--r--deps/v8/src/token.h8
-rw-r--r--deps/v8/src/type-info.cc229
-rw-r--r--deps/v8/src/type-info.h33
-rw-r--r--deps/v8/src/unicode.cc10
-rw-r--r--deps/v8/src/unicode.h4
-rw-r--r--deps/v8/src/uri.js47
-rw-r--r--deps/v8/src/utils.h83
-rw-r--r--deps/v8/src/v8-counters.h9
-rw-r--r--deps/v8/src/v8.cc81
-rw-r--r--deps/v8/src/v8.h26
-rw-r--r--deps/v8/src/v8conversions.h4
-rw-r--r--deps/v8/src/v8globals.h185
-rw-r--r--deps/v8/src/v8memory.h6
-rw-r--r--deps/v8/src/v8natives.js326
-rw-r--r--deps/v8/src/v8threads.cc2
-rw-r--r--deps/v8/src/v8utils.cc6
-rw-r--r--deps/v8/src/v8utils.h12
-rw-r--r--deps/v8/src/variables.cc26
-rw-r--r--deps/v8/src/variables.h73
-rw-r--r--deps/v8/src/version.cc6
-rw-r--r--deps/v8/src/weakmap.js98
-rw-r--r--deps/v8/src/win32-headers.h3
-rw-r--r--deps/v8/src/x64/assembler-x64-inl.h50
-rw-r--r--deps/v8/src/x64/assembler-x64.cc183
-rw-r--r--deps/v8/src/x64/assembler-x64.h32
-rw-r--r--deps/v8/src/x64/builtins-x64.cc1238
-rw-r--r--deps/v8/src/x64/code-stubs-x64.cc1889
-rw-r--r--deps/v8/src/x64/code-stubs-x64.h287
-rw-r--r--deps/v8/src/x64/codegen-x64.cc334
-rw-r--r--deps/v8/src/x64/codegen-x64.h15
-rw-r--r--deps/v8/src/x64/cpu-x64.cc2
-rw-r--r--deps/v8/src/x64/debug-x64.cc160
-rw-r--r--deps/v8/src/x64/deoptimizer-x64.cc173
-rw-r--r--deps/v8/src/x64/disasm-x64.cc20
-rw-r--r--deps/v8/src/x64/frames-x64.h27
-rw-r--r--deps/v8/src/x64/full-codegen-x64.cc844
-rw-r--r--deps/v8/src/x64/ic-x64.cc379
-rw-r--r--deps/v8/src/x64/lithium-codegen-x64.cc956
-rw-r--r--deps/v8/src/x64/lithium-codegen-x64.h51
-rw-r--r--deps/v8/src/x64/lithium-gap-resolver-x64.cc5
-rw-r--r--deps/v8/src/x64/lithium-x64.cc290
-rw-r--r--deps/v8/src/x64/lithium-x64.h145
-rw-r--r--deps/v8/src/x64/macro-assembler-x64.cc1169
-rw-r--r--deps/v8/src/x64/macro-assembler-x64.h410
-rw-r--r--deps/v8/src/x64/regexp-macro-assembler-x64.cc29
-rw-r--r--deps/v8/src/x64/stub-cache-x64.cc1665
-rw-r--r--deps/v8/src/zone-inl.h15
-rw-r--r--deps/v8/src/zone.cc6
-rw-r--r--deps/v8/src/zone.h11
-rw-r--r--deps/v8/test/cctest/SConscript3
-rw-r--r--deps/v8/test/cctest/cctest.gyp10
-rw-r--r--deps/v8/test/cctest/cctest.h2
-rw-r--r--deps/v8/test/cctest/cctest.status21
-rw-r--r--deps/v8/test/cctest/test-accessors.cc2
-rw-r--r--deps/v8/test/cctest/test-alloc.cc44
-rw-r--r--deps/v8/test/cctest/test-api.cc1285
-rw-r--r--deps/v8/test/cctest/test-assembler-ia32.cc94
-rw-r--r--deps/v8/test/cctest/test-assembler-x64.cc95
-rw-r--r--deps/v8/test/cctest/test-ast.cc5
-rw-r--r--deps/v8/test/cctest/test-compiler.cc14
-rw-r--r--deps/v8/test/cctest/test-cpu-profiler.cc4
-rw-r--r--deps/v8/test/cctest/test-debug.cc163
-rw-r--r--deps/v8/test/cctest/test-decls.cc40
-rw-r--r--deps/v8/test/cctest/test-deoptimization.cc4
-rw-r--r--deps/v8/test/cctest/test-dictionary.cc69
-rw-r--r--deps/v8/test/cctest/test-disasm-arm.cc14
-rw-r--r--deps/v8/test/cctest/test-disasm-ia32.cc53
-rw-r--r--deps/v8/test/cctest/test-disasm-mips.cc6
-rw-r--r--deps/v8/test/cctest/test-disasm-x64.cc429
-rw-r--r--deps/v8/test/cctest/test-heap-profiler.cc264
-rw-r--r--deps/v8/test/cctest/test-heap.cc571
-rw-r--r--deps/v8/test/cctest/test-lockers.cc89
-rw-r--r--deps/v8/test/cctest/test-log.cc2
-rw-r--r--deps/v8/test/cctest/test-mark-compact.cc139
-rwxr-xr-xdeps/v8/test/cctest/test-parsing.cc251
-rw-r--r--deps/v8/test/cctest/test-platform-linux.cc2
-rw-r--r--deps/v8/test/cctest/test-platform-win32.cc2
-rw-r--r--deps/v8/test/cctest/test-profile-generator.cc2
-rw-r--r--deps/v8/test/cctest/test-regexp.cc9
-rw-r--r--deps/v8/test/cctest/test-reloc-info.cc2
-rw-r--r--deps/v8/test/cctest/test-serialize.cc40
-rw-r--r--deps/v8/test/cctest/test-sockets.cc2
-rw-r--r--deps/v8/test/cctest/test-spaces.cc104
-rw-r--r--deps/v8/test/cctest/test-strings.cc33
-rw-r--r--deps/v8/test/cctest/test-threads.cc22
-rw-r--r--deps/v8/test/cctest/test-utils.cc2
-rw-r--r--deps/v8/test/cctest/test-weakmaps.cc35
-rw-r--r--deps/v8/test/es5conform/es5conform.status39
-rw-r--r--deps/v8/test/message/message.status7
-rw-r--r--deps/v8/test/mjsunit/apply.js7
-rw-r--r--deps/v8/test/mjsunit/array-construct-transition.js39
-rw-r--r--deps/v8/test/mjsunit/array-join.js10
-rw-r--r--deps/v8/test/mjsunit/array-literal-transitions.js210
-rw-r--r--deps/v8/test/mjsunit/array-tostring.js159
-rw-r--r--deps/v8/test/mjsunit/assert-opt-and-deopt.js6
-rw-r--r--deps/v8/test/mjsunit/bugs/bug-618.js2
-rw-r--r--deps/v8/test/mjsunit/bugs/harmony/debug-blockscopes.js2
-rw-r--r--deps/v8/test/mjsunit/compiler/compare.js4
-rw-r--r--deps/v8/test/mjsunit/compiler/inline-arity-mismatch.js62
-rw-r--r--deps/v8/test/mjsunit/compiler/inline-context-slots.js49
-rw-r--r--deps/v8/test/mjsunit/compiler/lazy-const-lookup.js41
-rw-r--r--deps/v8/test/mjsunit/compiler/math-floor-global.js161
-rw-r--r--deps/v8/test/mjsunit/compiler/math-floor-local.js161
-rw-r--r--deps/v8/test/mjsunit/compiler/regress-96989.js43
-rw-r--r--deps/v8/test/mjsunit/compiler/regress-deopt-call-as-function.js62
-rw-r--r--deps/v8/test/mjsunit/compiler/regress-funarguments.js20
-rw-r--r--deps/v8/test/mjsunit/compiler/regress-inline-callfunctionstub.js46
-rw-r--r--deps/v8/test/mjsunit/compiler/strict-recompile.js51
-rw-r--r--deps/v8/test/mjsunit/const-redecl.js62
-rw-r--r--deps/v8/test/mjsunit/cyclic-error-to-string.js46
-rw-r--r--deps/v8/test/mjsunit/d8-os.js130
-rw-r--r--deps/v8/test/mjsunit/date.js10
-rw-r--r--deps/v8/test/mjsunit/debug-break-inline.js100
-rw-r--r--deps/v8/test/mjsunit/debug-evaluate-locals-optimized-double.js174
-rw-r--r--deps/v8/test/mjsunit/debug-evaluate-locals-optimized.js160
-rw-r--r--deps/v8/test/mjsunit/debug-scopes.js49
-rw-r--r--deps/v8/test/mjsunit/debug-step-3.js94
-rw-r--r--deps/v8/test/mjsunit/debug-stepout-scope.js423
-rw-r--r--deps/v8/test/mjsunit/element-kind.js102
-rw-r--r--deps/v8/test/mjsunit/elements-kind-depends.js74
-rw-r--r--deps/v8/test/mjsunit/elements-kind.js344
-rw-r--r--deps/v8/test/mjsunit/elements-transition-hoisting.js168
-rw-r--r--deps/v8/test/mjsunit/elements-transition.js113
-rw-r--r--deps/v8/test/mjsunit/error-tostring.js85
-rw-r--r--deps/v8/test/mjsunit/eval.js49
-rw-r--r--deps/v8/test/mjsunit/external-array.js46
-rw-r--r--deps/v8/test/mjsunit/function-bind.js197
-rw-r--r--deps/v8/test/mjsunit/function-named-self-reference.js45
-rw-r--r--deps/v8/test/mjsunit/fuzz-natives.js3
-rw-r--r--deps/v8/test/mjsunit/global-const-var-conflicts.js10
-rw-r--r--deps/v8/test/mjsunit/harmony/block-conflicts.js10
-rw-r--r--deps/v8/test/mjsunit/harmony/block-const-assign.js131
-rw-r--r--deps/v8/test/mjsunit/harmony/block-early-errors.js55
-rw-r--r--deps/v8/test/mjsunit/harmony/block-for.js146
-rw-r--r--deps/v8/test/mjsunit/harmony/block-leave.js84
-rw-r--r--deps/v8/test/mjsunit/harmony/block-let-crankshaft.js203
-rw-r--r--deps/v8/test/mjsunit/harmony/block-let-declaration.js80
-rw-r--r--deps/v8/test/mjsunit/harmony/block-let-semantics.js60
-rw-r--r--deps/v8/test/mjsunit/harmony/block-scoping.js59
-rw-r--r--deps/v8/test/mjsunit/harmony/collections.js314
-rw-r--r--deps/v8/test/mjsunit/harmony/debug-blockscopes.js148
-rw-r--r--deps/v8/test/mjsunit/harmony/debug-evaluate-blockscopes.js10
-rw-r--r--deps/v8/test/mjsunit/harmony/proxies-example-membrane.js512
-rw-r--r--deps/v8/test/mjsunit/harmony/proxies-for.js168
-rw-r--r--deps/v8/test/mjsunit/harmony/proxies-function.js748
-rw-r--r--deps/v8/test/mjsunit/harmony/proxies-hash.js122
-rw-r--r--deps/v8/test/mjsunit/harmony/proxies.js997
-rw-r--r--deps/v8/test/mjsunit/harmony/weakmaps.js167
-rw-r--r--deps/v8/test/mjsunit/math-min-max.js64
-rw-r--r--deps/v8/test/mjsunit/math-pow.js257
-rw-r--r--deps/v8/test/mjsunit/mjsunit.js2
-rw-r--r--deps/v8/test/mjsunit/mjsunit.status51
-rw-r--r--deps/v8/test/mjsunit/object-define-properties.js16
-rw-r--r--deps/v8/test/mjsunit/optimized-typeof.js47
-rw-r--r--deps/v8/test/mjsunit/regexp-static.js17
-rw-r--r--deps/v8/test/mjsunit/regress/regress-100702.js44
-rw-r--r--deps/v8/test/mjsunit/regress/regress-108296.js52
-rw-r--r--deps/v8/test/mjsunit/regress/regress-109195.js65
-rw-r--r--deps/v8/test/mjsunit/regress/regress-110509.js41
-rw-r--r--deps/v8/test/mjsunit/regress/regress-1110.js5
-rw-r--r--deps/v8/test/mjsunit/regress/regress-1170.js2
-rw-r--r--deps/v8/test/mjsunit/regress/regress-1213575.js11
-rw-r--r--deps/v8/test/mjsunit/regress/regress-1217.js50
-rw-r--r--deps/v8/test/mjsunit/regress/regress-1229.js107
-rw-r--r--deps/v8/test/mjsunit/regress/regress-1415.js42
-rw-r--r--deps/v8/test/mjsunit/regress/regress-1530.js69
-rw-r--r--deps/v8/test/mjsunit/regress/regress-1639-2.js93
-rw-r--r--deps/v8/test/mjsunit/regress/regress-1692.js89
-rw-r--r--deps/v8/test/mjsunit/regress/regress-1708.js63
-rw-r--r--deps/v8/test/mjsunit/regress/regress-1711.js38
-rw-r--r--deps/v8/test/mjsunit/regress/regress-1713.js127
-rw-r--r--deps/v8/test/mjsunit/regress/regress-1748.js35
-rw-r--r--deps/v8/test/mjsunit/regress/regress-1757.js32
-rw-r--r--deps/v8/test/mjsunit/regress/regress-1849.js39
-rw-r--r--deps/v8/test/mjsunit/regress/regress-1878.js34
-rw-r--r--deps/v8/test/mjsunit/regress/regress-1898.js37
-rw-r--r--deps/v8/test/mjsunit/regress/regress-1924.js42
-rw-r--r--deps/v8/test/mjsunit/regress/regress-221.js34
-rw-r--r--deps/v8/test/mjsunit/regress/regress-397.js17
-rw-r--r--deps/v8/test/mjsunit/regress/regress-877615.js12
-rw-r--r--deps/v8/test/mjsunit/regress/regress-91517.js112
-rw-r--r--deps/v8/test/mjsunit/regress/regress-94873.js78
-rw-r--r--deps/v8/test/mjsunit/regress/regress-95113.js2
-rw-r--r--deps/v8/test/mjsunit/regress/regress-98773.js39
-rw-r--r--deps/v8/test/mjsunit/regress/regress-99167.js33
-rw-r--r--deps/v8/test/mjsunit/regress/regress-crbug-100859.js39
-rw-r--r--deps/v8/test/mjsunit/regress/regress-crbug-107996.js64
-rw-r--r--deps/v8/test/mjsunit/regress/regress-debug-code-recompilation.js47
-rw-r--r--deps/v8/test/mjsunit/regress/regress-deopt-gc.js2
-rw-r--r--deps/v8/test/mjsunit/regress/regress-smi-only-concat.js37
-rw-r--r--deps/v8/test/mjsunit/regress/short-circuit.js32
-rw-r--r--deps/v8/test/mjsunit/stack-traces-2.js87
-rw-r--r--deps/v8/test/mjsunit/stack-traces.js58
-rw-r--r--deps/v8/test/mjsunit/strict-mode-implicit-receiver.js7
-rw-r--r--deps/v8/test/mjsunit/strict-mode.js6
-rw-r--r--deps/v8/test/mjsunit/string-external-cached.js121
-rw-r--r--deps/v8/test/mjsunit/string-externalize.js2
-rw-r--r--deps/v8/test/mjsunit/string-replace-one-char.js92
-rw-r--r--deps/v8/test/mjsunit/string-slices-regexp.js7
-rwxr-xr-xdeps/v8/test/mjsunit/string-slices.js72
-rw-r--r--deps/v8/test/mjsunit/switch.js173
-rw-r--r--deps/v8/test/mjsunit/to_number_order.js4
-rw-r--r--deps/v8/test/mjsunit/unbox-double-arrays.js55
-rw-r--r--deps/v8/test/mjsunit/undeletable-functions.js42
-rw-r--r--deps/v8/test/mozilla/mozilla.status90
-rw-r--r--deps/v8/test/preparser/preparser.status6
-rw-r--r--deps/v8/test/preparser/strict-identifiers.pyt55
-rw-r--r--deps/v8/test/sputnik/sputnik.status92
-rw-r--r--deps/v8/test/test262/README4
-rw-r--r--deps/v8/test/test262/test262.status1499
-rw-r--r--deps/v8/test/test262/testcfg.py35
-rw-r--r--deps/v8/tools/bash-completion.sh55
-rwxr-xr-xdeps/v8/tools/gc-nvp-trace-processor.py63
-rw-r--r--deps/v8/tools/gcmole/gccause.lua2
-rw-r--r--deps/v8/tools/gen-postmortem-metadata.py478
-rwxr-xr-xdeps/v8/tools/grokdump.py441
-rw-r--r--deps/v8/tools/gyp/v8-node.gyp13
-rw-r--r--deps/v8/tools/gyp/v8.gyp144
-rw-r--r--deps/v8/tools/js2c.py5
-rwxr-xr-xdeps/v8/tools/linux-tick-processor31
-rwxr-xr-xdeps/v8/tools/ll_prof.py25
-rw-r--r--deps/v8/tools/logreader.js7
-rw-r--r--deps/v8/tools/merge-to-branch.sh361
-rwxr-xr-xdeps/v8/tools/presubmit.py60
-rwxr-xr-xdeps/v8/tools/push-to-trunk.sh53
-rwxr-xr-xdeps/v8/tools/test-wrapper-gypbuild.py32
-rwxr-xr-xdeps/v8/tools/test.py6
-rw-r--r--deps/v8/tools/utils.py2
-rw-r--r--doc/about/index.html4
-rw-r--r--doc/api/child_processes.markdown27
-rw-r--r--doc/api/cluster.markdown265
-rw-r--r--doc/api/crypto.markdown12
-rw-r--r--doc/api/debugger.markdown2
-rw-r--r--doc/api/fs.markdown43
-rw-r--r--doc/api/http.markdown111
-rw-r--r--doc/api/https.markdown8
-rw-r--r--doc/api/modules.markdown3
-rw-r--r--doc/api/net.markdown50
-rw-r--r--doc/api/path.markdown16
-rw-r--r--doc/api/process.markdown5
-rw-r--r--doc/api/querystring.markdown5
-rw-r--r--doc/api/readline.markdown130
-rw-r--r--doc/api/tls.markdown20
-rw-r--r--doc/api/zlib.markdown1
-rw-r--r--doc/api_assets/anchor.pngbin509 -> 0 bytes
-rw-r--r--doc/api_assets/style.css35
-rw-r--r--doc/community/index.html6
-rw-r--r--doc/footer-logo-alt.pngbin2037 -> 0 bytes
-rw-r--r--doc/icons-interior.pngbin5177 -> 0 bytes
-rw-r--r--doc/images/anchor.png (renamed from doc/anchor.png)bin509 -> 509 bytes
-rw-r--r--doc/images/close-downloads.png (renamed from doc/close-downloads.png)bin1232 -> 1232 bytes
-rw-r--r--doc/images/community-icons.png (renamed from doc/community-icons.png)bin6901 -> 6901 bytes
-rw-r--r--doc/images/download-logo.png (renamed from doc/download-logo.png)bin7614 -> 7614 bytes
-rw-r--r--doc/images/ebay-logo.png (renamed from doc/ebay-logo.png)bin1206 -> 1206 bytes
-rw-r--r--doc/images/footer-logo-alt.png (renamed from doc/api_assets/footer-logo-alt.png)bin2037 -> 2037 bytes
-rw-r--r--doc/images/footer-logo.png (renamed from doc/footer-logo.png)bin2626 -> 2626 bytes
-rw-r--r--doc/images/home-icons.png (renamed from doc/home-icons.png)bin2107 -> 2107 bytes
-rw-r--r--doc/images/icons-interior.png (renamed from doc/api_assets/icons-interior.png)bin5177 -> 5177 bytes
-rw-r--r--doc/images/icons.png (renamed from doc/icons.png)bin2107 -> 2107 bytes
-rw-r--r--doc/images/joyent-logo_orange_nodeorg-01.png (renamed from doc/joyent-logo_orange_nodeorg-01.png)bin2358 -> 2358 bytes
-rw-r--r--doc/images/linkedin-logo.png (renamed from doc/linkedin-logo.png)bin2346 -> 2346 bytes
-rw-r--r--doc/images/logo-light.png (renamed from doc/api_assets/logo-light.png)bin1448 -> 1448 bytes
-rw-r--r--doc/images/logo.png (renamed from doc/logo.png)bin5081 -> 5081 bytes
-rw-r--r--doc/images/logos/monitor.png (renamed from doc/logos/monitor.png)bin130547 -> 130547 bytes
-rw-r--r--doc/images/logos/node-favicon.png (renamed from doc/logos/node-favicon.png)bin212 -> 212 bytes
-rw-r--r--doc/images/logos/nodejs-1024x768.png (renamed from doc/logos/nodejs-1024x768.png)bin493710 -> 493710 bytes
-rw-r--r--doc/images/logos/nodejs-1280x1024.png (renamed from doc/logos/nodejs-1280x1024.png)bin817701 -> 817701 bytes
-rw-r--r--doc/images/logos/nodejs-1440x900.png (renamed from doc/logos/nodejs-1440x900.png)bin840815 -> 840815 bytes
-rw-r--r--doc/images/logos/nodejs-1920x1200.png (renamed from doc/logos/nodejs-1920x1200.png)bin1626767 -> 1626767 bytes
-rw-r--r--doc/images/logos/nodejs-2560x1440.png (renamed from doc/logos/nodejs-2560x1440.png)bin2168213 -> 2168213 bytes
-rw-r--r--doc/images/logos/nodejs-black.eps (renamed from doc/logos/nodejs-black.eps)bin587222 -> 587222 bytes
-rw-r--r--doc/images/logos/nodejs-black.png (renamed from doc/logos/nodejs-black.png)bin2630 -> 2630 bytes
-rw-r--r--doc/images/logos/nodejs-dark.eps (renamed from doc/logos/nodejs-dark.eps)bin674234 -> 674234 bytes
-rw-r--r--doc/images/logos/nodejs-dark.png (renamed from doc/logos/nodejs-dark.png)bin3501 -> 3501 bytes
-rw-r--r--doc/images/logos/nodejs-green.eps (renamed from doc/logos/nodejs-green.eps)bin669754 -> 669754 bytes
-rw-r--r--doc/images/logos/nodejs-green.png (renamed from doc/logos/nodejs-green.png)bin3452 -> 3452 bytes
-rw-r--r--doc/images/logos/nodejs-light.eps (renamed from doc/logos/nodejs-light.eps)bin588530 -> 588530 bytes
-rw-r--r--doc/images/logos/nodejs.png (renamed from doc/logos/nodejs.png)bin3026 -> 3026 bytes
-rw-r--r--doc/images/microsoft-logo.png (renamed from doc/microsoft-logo.png)bin2570 -> 2570 bytes
-rw-r--r--doc/images/not-invented-here.png (renamed from doc/community/not-invented-here.png)bin203717 -> 203717 bytes
-rw-r--r--doc/images/platform-icons.png (renamed from doc/api_assets/platform-icons.png)bin3593 -> 3593 bytes
-rw-r--r--doc/images/ryan-speaker.jpg (renamed from doc/ryan-speaker.jpg)bin11971 -> 11971 bytes
-rw-r--r--doc/images/sh_javascript.min.js (renamed from doc/api_assets/sh_javascript.min.js)0
-rw-r--r--doc/images/sh_main.js (renamed from doc/api_assets/sh_main.js)0
-rw-r--r--doc/images/sponsored.png (renamed from doc/sponsored.png)bin5285 -> 5285 bytes
-rw-r--r--doc/images/twitter-bird.png (renamed from doc/api_assets/twitter-bird.png)bin351 -> 351 bytes
-rw-r--r--doc/images/yahoo-logo.png (renamed from doc/yahoo-logo.png)bin2581 -> 2581 bytes
-rw-r--r--doc/index.html32
-rw-r--r--doc/logo-light.pngbin1448 -> 0 bytes
-rw-r--r--doc/logos/index.html26
-rw-r--r--doc/pipe.css36
-rw-r--r--doc/platform-icons.pngbin3593 -> 0 bytes
-rw-r--r--doc/robots.txt6
-rw-r--r--doc/template.html12
-rw-r--r--doc/twitter-bird.pngbin351 -> 0 bytes
-rw-r--r--lib/_debugger.js74
-rw-r--r--lib/assert.js14
-rw-r--r--lib/child_process.js107
-rw-r--r--lib/cluster.js505
-rw-r--r--lib/fs.js118
-rw-r--r--lib/http.js212
-rw-r--r--lib/https.js4
-rw-r--r--lib/module.js4
-rw-r--r--lib/net.js121
-rw-r--r--lib/os.js3
-rw-r--r--lib/path.js16
-rw-r--r--lib/punycode.js40
-rw-r--r--lib/querystring.js23
-rw-r--r--lib/readline.js65
-rw-r--r--lib/repl.js12
-rw-r--r--lib/stream.js20
-rw-r--r--lib/sys.js37
-rw-r--r--lib/tls.js63
-rw-r--r--lib/tty.js4
-rw-r--r--lib/url.js75
-rw-r--r--lib/util.js33
-rw-r--r--lib/zlib.js13
-rw-r--r--node.gyp23
-rw-r--r--src/node.cc185
-rw-r--r--src/node.h52
-rw-r--r--src/node.js62
-rw-r--r--src/node_buffer.cc4
-rw-r--r--src/node_constants.cc4
-rw-r--r--src/node_crypto.cc160
-rw-r--r--src/node_extensions.h1
-rw-r--r--src/node_file.cc141
-rw-r--r--src/node_internals.h45
-rw-r--r--src/node_os.cc90
-rw-r--r--src/node_version.h4
-rw-r--r--src/node_zlib.cc100
-rw-r--r--src/platform.h49
-rw-r--r--src/platform_darwin.cc226
-rw-r--r--src/platform_darwin_proctitle.cc146
-rw-r--r--src/platform_freebsd.cc176
-rw-r--r--src/platform_linux.cc396
-rw-r--r--src/platform_none.cc52
-rw-r--r--src/platform_openbsd.cc176
-rw-r--r--src/platform_sunos.cc305
-rw-r--r--src/platform_win32.cc301
-rw-r--r--src/platform_win32.h79
-rw-r--r--src/udp_wrap.cc8
-rw-r--r--src/v8_typed_array.cc191
-rw-r--r--test/CMakeLists.txt88
-rw-r--r--test/addons/.gitignore5
-rw-r--r--test/addons/hello-world/binding.cc17
-rw-r--r--test/addons/hello-world/binding.gyp8
-rw-r--r--test/addons/hello-world/test.js4
-rw-r--r--test/simple/test-assert.js19
-rw-r--r--test/simple/test-child-process-disconnect.js106
-rw-r--r--test/simple/test-child-process-internal.js58
-rw-r--r--test/simple/test-child-process-silent.js105
-rw-r--r--test/simple/test-cluster-basic.js161
-rw-r--r--test/simple/test-cluster-fork-env.js63
-rw-r--r--test/simple/test-cluster-kill-workers.js84
-rw-r--r--test/simple/test-cluster-master-error.js132
-rw-r--r--test/simple/test-cluster-message.js130
-rw-r--r--test/simple/test-cluster-setup-master.js89
-rw-r--r--test/simple/test-crypto-padding.js119
-rw-r--r--test/simple/test-crypto.js5
-rw-r--r--test/simple/test-debugger-repl-utf8.js4
-rw-r--r--test/simple/test-debugger-repl.js4
-rw-r--r--test/simple/test-eio-limit.js49
-rw-r--r--test/simple/test-fs-append-file-sync.js92
-rw-r--r--test/simple/test-fs-append-file.js126
-rw-r--r--test/simple/test-fs-exists.js42
-rw-r--r--test/simple/test-fs-mkdir.js7
-rw-r--r--test/simple/test-fs-open-flags.js60
-rw-r--r--test/simple/test-http-1.0.js2
-rw-r--r--test/simple/test-http-after-connect.js89
-rw-r--r--test/simple/test-http-connect.js105
-rw-r--r--test/simple/test-http-date-header.js35
-rw-r--r--test/simple/test-http-max-headers-count.js87
-rw-r--r--test/simple/test-http-parser.js25
-rw-r--r--test/simple/test-http-pause.js75
-rw-r--r--test/simple/test-http-upgrade-agent.js8
-rw-r--r--test/simple/test-https-client-reject.js95
-rw-r--r--test/simple/test-net-connect-options.js58
-rw-r--r--test/simple/test-net-server-listen-remove-callback.js3
-rw-r--r--test/simple/test-os.js6
-rw-r--r--test/simple/test-path.js3
-rw-r--r--test/simple/test-querystring.js23
-rw-r--r--test/simple/test-repl.js3
-rw-r--r--test/simple/test-require-exceptions.js9
-rw-r--r--test/simple/test-stdin-resume-pause.js23
-rw-r--r--test/simple/test-stream-pipe-cleanup.js10
-rw-r--r--test/simple/test-sys.js2
-rw-r--r--test/simple/test-tls-client-abort.js2
-rw-r--r--test/simple/test-tls-client-reject.js92
-rw-r--r--test/simple/test-tls-client-resume.js4
-rw-r--r--test/simple/test-tls-client-verify.js3
-rw-r--r--test/simple/test-tls-connect-given-socket.js2
-rw-r--r--test/simple/test-tls-connect-simple.js4
-rw-r--r--test/simple/test-tls-connect.js2
-rw-r--r--test/simple/test-tls-npn-server-client.js9
-rw-r--r--test/simple/test-tls-passphrase.js6
-rw-r--r--test/simple/test-tls-pause-close.js2
-rw-r--r--test/simple/test-tls-pause.js2
-rw-r--r--test/simple/test-tls-peer-certificate.js2
-rw-r--r--test/simple/test-tls-remote.js2
-rw-r--r--test/simple/test-tls-request-timeout.js2
-rw-r--r--test/simple/test-tls-set-encoding.js2
-rw-r--r--test/simple/test-tls-sni-server-client.js8
-rw-r--r--test/simple/test-typed-arrays.js110
-rw-r--r--test/simple/test-url.js65
-rw-r--r--test/simple/test-zlib-dictionary.js95
-rw-r--r--tools/addon.gypi24
-rw-r--r--[-rwxr-xr-x]tools/gyp/PRESUBMIT.py10
-rwxr-xr-xtools/gyp/buildbot/buildbot_run.py83
-rwxr-xr-xtools/gyp/gyptest.py2
-rw-r--r--tools/gyp/pylib/gyp/MSVSNew.py8
-rw-r--r--tools/gyp/pylib/gyp/MSVSProject.py6
-rw-r--r--tools/gyp/pylib/gyp/MSVSSettings.py1
-rwxr-xr-x[-rw-r--r--]tools/gyp/pylib/gyp/MSVSSettings_test.py3
-rw-r--r--tools/gyp/pylib/gyp/MSVSToolFile.py2
-rw-r--r--tools/gyp/pylib/gyp/MSVSUserFile.py2
-rw-r--r--[-rwxr-xr-x]tools/gyp/pylib/gyp/MSVSVersion.py6
-rw-r--r--tools/gyp/pylib/gyp/SCons.py5
-rwxr-xr-x[-rw-r--r--]tools/gyp/pylib/gyp/__init__.py36
-rw-r--r--tools/gyp/pylib/gyp/common.py19
-rw-r--r--tools/gyp/pylib/gyp/easy_xml.py2
-rwxr-xr-x[-rw-r--r--]tools/gyp/pylib/gyp/easy_xml_test.py2
-rw-r--r--tools/gyp/pylib/gyp/generator/dump_dependency_json.py9
-rw-r--r--tools/gyp/pylib/gyp/generator/gypd.py2
-rw-r--r--tools/gyp/pylib/gyp/generator/gypsh.py2
-rw-r--r--tools/gyp/pylib/gyp/generator/make.py197
-rw-r--r--tools/gyp/pylib/gyp/generator/msvs.py2
-rwxr-xr-x[-rw-r--r--]tools/gyp/pylib/gyp/generator/msvs_test.py2
-rw-r--r--tools/gyp/pylib/gyp/generator/ninja.py236
-rw-r--r--tools/gyp/pylib/gyp/generator/scons.py2
-rw-r--r--tools/gyp/pylib/gyp/generator/xcode.py2
-rw-r--r--tools/gyp/pylib/gyp/input.py11
-rwxr-xr-x[-rw-r--r--]tools/gyp/pylib/gyp/mac_tool.py5
-rw-r--r--tools/gyp/pylib/gyp/ninja_syntax.py69
-rwxr-xr-x[-rw-r--r--]tools/gyp/pylib/gyp/sun_tool.py49
-rwxr-xr-x[-rw-r--r--]tools/gyp/pylib/gyp/system_test.py10
-rw-r--r--tools/gyp/pylib/gyp/xcodeproj_file.py8
-rw-r--r--tools/gyp/pylib/gyp/xml_fix.py1
-rw-r--r--tools/gyp/pylintrc307
-rwxr-xr-xtools/gyp/test/actions-bare/gyptest-bare.py23
-rw-r--r--tools/gyp/test/actions-bare/src/bare.gyp25
-rwxr-xr-xtools/gyp/test/actions-bare/src/bare.py11
-rwxr-xr-xtools/gyp/test/actions-multiple/gyptest-all.py42
-rw-r--r--tools/gyp/test/actions-multiple/src/actions.gyp165
-rwxr-xr-xtools/gyp/test/actions-multiple/src/copy.py9
-rwxr-xr-xtools/gyp/test/actions-multiple/src/filter.py12
-rw-r--r--tools/gyp/test/actions-multiple/src/foo.c11
-rw-r--r--tools/gyp/test/actions-multiple/src/input.txt1
-rw-r--r--tools/gyp/test/actions-multiple/src/main.c22
-rwxr-xr-xtools/gyp/test/actions-subdir/gyptest-action.py26
-rwxr-xr-xtools/gyp/test/actions-subdir/src/make-file.py11
-rw-r--r--tools/gyp/test/actions-subdir/src/none.gyp31
-rwxr-xr-xtools/gyp/test/actions-subdir/src/subdir/make-subdir-file.py11
-rw-r--r--tools/gyp/test/actions-subdir/src/subdir/subdir.gyp28
-rwxr-xr-xtools/gyp/test/actions/gyptest-all.py101
-rwxr-xr-xtools/gyp/test/actions/gyptest-default.py68
-rwxr-xr-xtools/gyp/test/actions/gyptest-errors.py24
-rw-r--r--tools/gyp/test/actions/src/action_missing_name.gyp24
-rw-r--r--tools/gyp/test/actions/src/actions.gyp114
-rwxr-xr-xtools/gyp/test/actions/src/confirm-dep-files.py21
-rwxr-xr-xtools/gyp/test/actions/src/subdir1/counter.py46
-rw-r--r--tools/gyp/test/actions/src/subdir1/executable.gyp74
-rwxr-xr-xtools/gyp/test/actions/src/subdir1/make-prog1.py20
-rwxr-xr-xtools/gyp/test/actions/src/subdir1/make-prog2.py20
-rw-r--r--tools/gyp/test/actions/src/subdir1/program.c12
-rwxr-xr-xtools/gyp/test/actions/src/subdir2/make-file.py11
-rw-r--r--tools/gyp/test/actions/src/subdir2/none.gyp33
-rwxr-xr-xtools/gyp/test/actions/src/subdir3/generate_main.py21
-rw-r--r--tools/gyp/test/actions/src/subdir3/null_input.gyp29
-rwxr-xr-xtools/gyp/test/additional-targets/gyptest-additional.py55
-rw-r--r--tools/gyp/test/additional-targets/src/all.gyp13
-rw-r--r--tools/gyp/test/additional-targets/src/dir1/actions.gyp56
-rwxr-xr-xtools/gyp/test/additional-targets/src/dir1/emit.py11
-rw-r--r--tools/gyp/test/additional-targets/src/dir1/lib1.c6
-rwxr-xr-xtools/gyp/test/assembly/gyptest-assembly.py31
-rw-r--r--tools/gyp/test/assembly/src/as.bat4
-rw-r--r--tools/gyp/test/assembly/src/assembly.gyp59
-rw-r--r--tools/gyp/test/assembly/src/lib1.S10
-rw-r--r--tools/gyp/test/assembly/src/lib1.c3
-rw-r--r--tools/gyp/test/assembly/src/program.c12
-rwxr-xr-xtools/gyp/test/builddir/gyptest-all.py77
-rwxr-xr-xtools/gyp/test/builddir/gyptest-default.py77
-rw-r--r--tools/gyp/test/builddir/src/builddir.gypi21
-rw-r--r--tools/gyp/test/builddir/src/func1.c6
-rw-r--r--tools/gyp/test/builddir/src/func2.c6
-rw-r--r--tools/gyp/test/builddir/src/func3.c6
-rw-r--r--tools/gyp/test/builddir/src/func4.c6
-rw-r--r--tools/gyp/test/builddir/src/func5.c6
-rw-r--r--tools/gyp/test/builddir/src/prog1.c10
-rw-r--r--tools/gyp/test/builddir/src/prog1.gyp30
-rw-r--r--tools/gyp/test/builddir/src/subdir2/prog2.c10
-rw-r--r--tools/gyp/test/builddir/src/subdir2/prog2.gyp19
-rw-r--r--tools/gyp/test/builddir/src/subdir2/subdir3/prog3.c10
-rw-r--r--tools/gyp/test/builddir/src/subdir2/subdir3/prog3.gyp19
-rw-r--r--tools/gyp/test/builddir/src/subdir2/subdir3/subdir4/prog4.c10
-rw-r--r--tools/gyp/test/builddir/src/subdir2/subdir3/subdir4/prog4.gyp19
-rw-r--r--tools/gyp/test/builddir/src/subdir2/subdir3/subdir4/subdir5/prog5.c10
-rw-r--r--tools/gyp/test/builddir/src/subdir2/subdir3/subdir4/subdir5/prog5.gyp19
-rw-r--r--tools/gyp/test/cflags/cflags.c15
-rw-r--r--tools/gyp/test/cflags/cflags.gyp16
-rwxr-xr-xtools/gyp/test/cflags/gyptest-cflags.py65
-rwxr-xr-xtools/gyp/test/compilable/gyptest-headers.py29
-rw-r--r--tools/gyp/test/compilable/src/headers.gyp26
-rw-r--r--tools/gyp/test/compilable/src/lib1.cpp7
-rw-r--r--tools/gyp/test/compilable/src/lib1.hpp6
-rw-r--r--tools/gyp/test/compilable/src/program.cpp9
-rw-r--r--tools/gyp/test/configurations/basics/configurations.c15
-rw-r--r--tools/gyp/test/configurations/basics/configurations.gyp32
-rwxr-xr-xtools/gyp/test/configurations/basics/gyptest-configurations.py29
-rw-r--r--tools/gyp/test/configurations/inheritance/configurations.c21
-rw-r--r--tools/gyp/test/configurations/inheritance/configurations.gyp40
-rwxr-xr-xtools/gyp/test/configurations/inheritance/gyptest-inheritance.py33
-rw-r--r--tools/gyp/test/configurations/invalid/actions.gyp18
-rw-r--r--tools/gyp/test/configurations/invalid/all_dependent_settings.gyp18
-rw-r--r--tools/gyp/test/configurations/invalid/configurations.gyp18
-rw-r--r--tools/gyp/test/configurations/invalid/dependencies.gyp18
-rw-r--r--tools/gyp/test/configurations/invalid/direct_dependent_settings.gyp18
-rwxr-xr-xtools/gyp/test/configurations/invalid/gyptest-configurations.py38
-rw-r--r--tools/gyp/test/configurations/invalid/libraries.gyp18
-rw-r--r--tools/gyp/test/configurations/invalid/link_settings.gyp18
-rw-r--r--tools/gyp/test/configurations/invalid/sources.gyp18
-rw-r--r--tools/gyp/test/configurations/invalid/target_name.gyp18
-rw-r--r--tools/gyp/test/configurations/invalid/type.gyp18
-rw-r--r--tools/gyp/test/configurations/target_platform/configurations.gyp58
-rw-r--r--tools/gyp/test/configurations/target_platform/front.c8
-rwxr-xr-xtools/gyp/test/configurations/target_platform/gyptest-target_platform.py40
-rw-r--r--tools/gyp/test/configurations/target_platform/left.c3
-rw-r--r--tools/gyp/test/configurations/target_platform/right.c3
-rw-r--r--tools/gyp/test/configurations/x64/configurations.c12
-rw-r--r--tools/gyp/test/configurations/x64/configurations.gyp26
-rwxr-xr-xtools/gyp/test/configurations/x64/gyptest-x86.py29
-rwxr-xr-xtools/gyp/test/copies/gyptest-all.py40
-rwxr-xr-xtools/gyp/test/copies/gyptest-default.py40
-rwxr-xr-xtools/gyp/test/copies/gyptest-slash.py38
-rw-r--r--tools/gyp/test/copies/src/copies-slash.gyp36
-rw-r--r--tools/gyp/test/copies/src/copies.gyp70
-rw-r--r--tools/gyp/test/copies/src/directory/file31
-rw-r--r--tools/gyp/test/copies/src/directory/file41
-rw-r--r--tools/gyp/test/copies/src/directory/subdir/file51
-rw-r--r--tools/gyp/test/copies/src/file11
-rw-r--r--tools/gyp/test/copies/src/file21
-rw-r--r--tools/gyp/test/copies/src/parentdir/subdir/file61
-rw-r--r--tools/gyp/test/cxxflags/cxxflags.cc15
-rw-r--r--tools/gyp/test/cxxflags/cxxflags.gyp16
-rwxr-xr-xtools/gyp/test/cxxflags/gyptest-cxxflags.py65
-rw-r--r--tools/gyp/test/defines-escaping/defines-escaping.c11
-rw-r--r--tools/gyp/test/defines-escaping/defines-escaping.gyp19
-rwxr-xr-xtools/gyp/test/defines-escaping/gyptest-defines-escaping.py184
-rw-r--r--tools/gyp/test/defines/defines-env.gyp22
-rw-r--r--tools/gyp/test/defines/defines.c18
-rw-r--r--tools/gyp/test/defines/defines.gyp37
-rwxr-xr-xtools/gyp/test/defines/gyptest-define-override.py34
-rwxr-xr-xtools/gyp/test/defines/gyptest-defines-env-regyp.py50
-rwxr-xr-xtools/gyp/test/defines/gyptest-defines-env.py85
-rwxr-xr-xtools/gyp/test/defines/gyptest-defines.py26
-rwxr-xr-xtools/gyp/test/dependencies/a.c9
-rwxr-xr-xtools/gyp/test/dependencies/b/b.c3
-rwxr-xr-xtools/gyp/test/dependencies/b/b.gyp22
-rwxr-xr-xtools/gyp/test/dependencies/b/b3.c9
-rw-r--r--tools/gyp/test/dependencies/c/c.c4
-rw-r--r--tools/gyp/test/dependencies/c/c.gyp22
-rw-r--r--tools/gyp/test/dependencies/c/d.c3
-rw-r--r--tools/gyp/test/dependencies/extra_targets.gyp18
-rwxr-xr-xtools/gyp/test/dependencies/gyptest-extra-targets.py21
-rwxr-xr-xtools/gyp/test/dependencies/gyptest-lib-only.py39
-rwxr-xr-xtools/gyp/test/dependencies/gyptest-none-traversal.py25
-rwxr-xr-xtools/gyp/test/dependencies/lib_only.gyp16
-rw-r--r--tools/gyp/test/dependencies/main.c14
-rwxr-xr-xtools/gyp/test/dependencies/none_traversal.gyp46
-rwxr-xr-xtools/gyp/test/dependency-copy/gyptest-copy.py26
-rw-r--r--tools/gyp/test/dependency-copy/src/copies.gyp25
-rw-r--r--tools/gyp/test/dependency-copy/src/file1.c7
-rw-r--r--tools/gyp/test/dependency-copy/src/file2.c7
-rw-r--r--tools/gyp/test/exclusion/exclusion.gyp23
-rwxr-xr-xtools/gyp/test/exclusion/gyptest-exclusion.py22
-rw-r--r--tools/gyp/test/exclusion/hello.c15
-rw-r--r--tools/gyp/test/generator-output/actions/actions.gyp16
-rw-r--r--tools/gyp/test/generator-output/actions/build/README.txt4
-rw-r--r--tools/gyp/test/generator-output/actions/subdir1/actions-out/README.txt4
-rw-r--r--tools/gyp/test/generator-output/actions/subdir1/build/README.txt4
-rw-r--r--tools/gyp/test/generator-output/actions/subdir1/executable.gyp44
-rwxr-xr-xtools/gyp/test/generator-output/actions/subdir1/make-prog1.py20
-rwxr-xr-xtools/gyp/test/generator-output/actions/subdir1/make-prog2.py20
-rw-r--r--tools/gyp/test/generator-output/actions/subdir1/program.c12
-rw-r--r--tools/gyp/test/generator-output/actions/subdir2/actions-out/README.txt4
-rw-r--r--tools/gyp/test/generator-output/actions/subdir2/build/README.txt4
-rwxr-xr-xtools/gyp/test/generator-output/actions/subdir2/make-file.py11
-rw-r--r--tools/gyp/test/generator-output/actions/subdir2/none.gyp31
-rw-r--r--tools/gyp/test/generator-output/copies/build/README.txt4
-rw-r--r--tools/gyp/test/generator-output/copies/copies-out/README.txt4
-rw-r--r--tools/gyp/test/generator-output/copies/copies.gyp50
-rw-r--r--tools/gyp/test/generator-output/copies/file11
-rw-r--r--tools/gyp/test/generator-output/copies/file21
-rw-r--r--tools/gyp/test/generator-output/copies/subdir/build/README.txt4
-rw-r--r--tools/gyp/test/generator-output/copies/subdir/copies-out/README.txt4
-rw-r--r--tools/gyp/test/generator-output/copies/subdir/file31
-rw-r--r--tools/gyp/test/generator-output/copies/subdir/file41
-rw-r--r--tools/gyp/test/generator-output/copies/subdir/subdir.gyp32
-rwxr-xr-xtools/gyp/test/generator-output/gyptest-actions.py58
-rwxr-xr-xtools/gyp/test/generator-output/gyptest-copies.py59
-rwxr-xr-xtools/gyp/test/generator-output/gyptest-relocate.py60
-rwxr-xr-xtools/gyp/test/generator-output/gyptest-rules.py58
-rwxr-xr-xtools/gyp/test/generator-output/gyptest-subdir2-deep.py36
-rwxr-xr-xtools/gyp/test/generator-output/gyptest-top-all.py53
-rw-r--r--tools/gyp/test/generator-output/rules/build/README.txt4
-rwxr-xr-xtools/gyp/test/generator-output/rules/copy-file.py12
-rw-r--r--tools/gyp/test/generator-output/rules/rules.gyp16
-rw-r--r--tools/gyp/test/generator-output/rules/subdir1/build/README.txt4
-rw-r--r--tools/gyp/test/generator-output/rules/subdir1/define3.in01
-rw-r--r--tools/gyp/test/generator-output/rules/subdir1/define4.in01
-rw-r--r--tools/gyp/test/generator-output/rules/subdir1/executable.gyp59
-rw-r--r--tools/gyp/test/generator-output/rules/subdir1/function1.in16
-rw-r--r--tools/gyp/test/generator-output/rules/subdir1/function2.in16
-rw-r--r--tools/gyp/test/generator-output/rules/subdir1/program.c18
-rw-r--r--tools/gyp/test/generator-output/rules/subdir2/build/README.txt4
-rw-r--r--tools/gyp/test/generator-output/rules/subdir2/file1.in01
-rw-r--r--tools/gyp/test/generator-output/rules/subdir2/file2.in01
-rw-r--r--tools/gyp/test/generator-output/rules/subdir2/file3.in11
-rw-r--r--tools/gyp/test/generator-output/rules/subdir2/file4.in11
-rw-r--r--tools/gyp/test/generator-output/rules/subdir2/none.gyp49
-rw-r--r--tools/gyp/test/generator-output/rules/subdir2/rules-out/README.txt4
-rw-r--r--tools/gyp/test/generator-output/src/build/README.txt4
-rw-r--r--tools/gyp/test/generator-output/src/inc.h1
-rw-r--r--tools/gyp/test/generator-output/src/inc1/include1.h1
-rw-r--r--tools/gyp/test/generator-output/src/prog1.c18
-rw-r--r--tools/gyp/test/generator-output/src/prog1.gyp28
-rw-r--r--tools/gyp/test/generator-output/src/subdir2/build/README.txt4
-rw-r--r--tools/gyp/test/generator-output/src/subdir2/deeper/build/README.txt4
-rw-r--r--tools/gyp/test/generator-output/src/subdir2/deeper/deeper.c7
-rw-r--r--tools/gyp/test/generator-output/src/subdir2/deeper/deeper.gyp18
-rw-r--r--tools/gyp/test/generator-output/src/subdir2/deeper/deeper.h1
-rw-r--r--tools/gyp/test/generator-output/src/subdir2/inc2/include2.h1
-rw-r--r--tools/gyp/test/generator-output/src/subdir2/prog2.c18
-rw-r--r--tools/gyp/test/generator-output/src/subdir2/prog2.gyp28
-rw-r--r--tools/gyp/test/generator-output/src/subdir3/build/README.txt4
-rw-r--r--tools/gyp/test/generator-output/src/subdir3/inc3/include3.h1
-rw-r--r--tools/gyp/test/generator-output/src/subdir3/prog3.c18
-rw-r--r--tools/gyp/test/generator-output/src/subdir3/prog3.gyp25
-rw-r--r--tools/gyp/test/generator-output/src/symroot.gypi16
-rwxr-xr-xtools/gyp/test/hard_dependency/gyptest-exported-hard-dependency.py37
-rwxr-xr-xtools/gyp/test/hard_dependency/gyptest-no-exported-hard-dependency.py36
-rw-r--r--tools/gyp/test/hard_dependency/src/a.c9
-rw-r--r--tools/gyp/test/hard_dependency/src/a.h12
-rw-r--r--tools/gyp/test/hard_dependency/src/b.c9
-rw-r--r--tools/gyp/test/hard_dependency/src/b.h12
-rw-r--r--tools/gyp/test/hard_dependency/src/c.c9
-rw-r--r--tools/gyp/test/hard_dependency/src/c.h10
-rw-r--r--tools/gyp/test/hard_dependency/src/d.c9
-rwxr-xr-xtools/gyp/test/hard_dependency/src/emit.py11
-rw-r--r--tools/gyp/test/hard_dependency/src/hard_dependency.gyp78
-rwxr-xr-xtools/gyp/test/hello/gyptest-all.py24
-rwxr-xr-xtools/gyp/test/hello/gyptest-default.py24
-rwxr-xr-xtools/gyp/test/hello/gyptest-disable-regyp.py32
-rwxr-xr-xtools/gyp/test/hello/gyptest-regyp.py32
-rwxr-xr-xtools/gyp/test/hello/gyptest-target.py24
-rw-r--r--tools/gyp/test/hello/hello.c11
-rw-r--r--tools/gyp/test/hello/hello.gyp15
-rw-r--r--tools/gyp/test/hello/hello2.c11
-rw-r--r--tools/gyp/test/hello/hello2.gyp15
-rwxr-xr-xtools/gyp/test/home_dot_gyp/gyptest-home-includes-regyp.py44
-rwxr-xr-xtools/gyp/test/home_dot_gyp/gyptest-home-includes.py30
-rw-r--r--tools/gyp/test/home_dot_gyp/home/.gyp/include.gypi5
-rw-r--r--tools/gyp/test/home_dot_gyp/home2/.gyp/include.gypi5
-rw-r--r--tools/gyp/test/home_dot_gyp/src/all.gyp22
-rw-r--r--tools/gyp/test/home_dot_gyp/src/printfoo.c7
-rwxr-xr-xtools/gyp/test/include_dirs/gyptest-all.py46
-rwxr-xr-xtools/gyp/test/include_dirs/gyptest-default.py46
-rw-r--r--tools/gyp/test/include_dirs/src/inc.h1
-rw-r--r--tools/gyp/test/include_dirs/src/inc1/include1.h1
-rw-r--r--tools/gyp/test/include_dirs/src/includes.c19
-rw-r--r--tools/gyp/test/include_dirs/src/includes.gyp27
-rw-r--r--tools/gyp/test/include_dirs/src/shadow1/shadow.h1
-rw-r--r--tools/gyp/test/include_dirs/src/shadow2/shadow.h1
-rw-r--r--tools/gyp/test/include_dirs/src/subdir/inc.h1
-rw-r--r--tools/gyp/test/include_dirs/src/subdir/inc2/include2.h1
-rw-r--r--tools/gyp/test/include_dirs/src/subdir/subdir_includes.c14
-rw-r--r--tools/gyp/test/include_dirs/src/subdir/subdir_includes.gyp20
-rwxr-xr-xtools/gyp/test/intermediate_dir/gyptest-intermediate-dir.py45
-rwxr-xr-xtools/gyp/test/intermediate_dir/src/script.py24
-rw-r--r--tools/gyp/test/intermediate_dir/src/test.gyp40
-rw-r--r--tools/gyp/test/intermediate_dir/src/test2.gyp40
-rw-r--r--tools/gyp/test/lib/README.txt17
-rw-r--r--tools/gyp/test/lib/TestCmd.py1594
-rw-r--r--tools/gyp/test/lib/TestCommon.py581
-rw-r--r--tools/gyp/test/lib/TestGyp.py817
-rwxr-xr-xtools/gyp/test/library/gyptest-shared-obj-install-path.py37
-rwxr-xr-xtools/gyp/test/library/gyptest-shared.py84
-rwxr-xr-xtools/gyp/test/library/gyptest-static.py84
-rw-r--r--tools/gyp/test/library/src/lib1.c10
-rw-r--r--tools/gyp/test/library/src/lib1_moveable.c10
-rw-r--r--tools/gyp/test/library/src/lib2.c10
-rw-r--r--tools/gyp/test/library/src/lib2_moveable.c10
-rw-r--r--tools/gyp/test/library/src/library.gyp58
-rw-r--r--tools/gyp/test/library/src/program.c15
-rw-r--r--tools/gyp/test/library/src/shared_dependency.gyp33
-rw-r--r--tools/gyp/test/link-objects/base.c6
-rw-r--r--tools/gyp/test/link-objects/extra.c5
-rwxr-xr-xtools/gyp/test/link-objects/gyptest-all.py28
-rw-r--r--tools/gyp/test/link-objects/link-objects.gyp24
-rw-r--r--tools/gyp/test/mac/app-bundle/TestApp/English.lproj/InfoPlist.strings3
-rw-r--r--tools/gyp/test/mac/app-bundle/TestApp/English.lproj/MainMenu.xib4119
-rw-r--r--tools/gyp/test/mac/app-bundle/TestApp/TestApp-Info.plist32
-rw-r--r--tools/gyp/test/mac/app-bundle/TestApp/TestAppAppDelegate.h13
-rw-r--r--tools/gyp/test/mac/app-bundle/TestApp/TestAppAppDelegate.m15
-rw-r--r--tools/gyp/test/mac/app-bundle/TestApp/main.m10
-rw-r--r--tools/gyp/test/mac/app-bundle/empty.c0
-rw-r--r--tools/gyp/test/mac/app-bundle/test.gyp39
-rw-r--r--tools/gyp/test/mac/debuginfo/file.c6
-rw-r--r--tools/gyp/test/mac/debuginfo/test.gyp82
-rw-r--r--tools/gyp/test/mac/framework/TestFramework/English.lproj/InfoPlist.strings2
-rw-r--r--tools/gyp/test/mac/framework/TestFramework/Info.plist28
-rw-r--r--tools/gyp/test/mac/framework/TestFramework/ObjCVector.h28
-rw-r--r--tools/gyp/test/mac/framework/TestFramework/ObjCVector.mm63
-rw-r--r--tools/gyp/test/mac/framework/TestFramework/ObjCVectorInternal.h9
-rw-r--r--tools/gyp/test/mac/framework/TestFramework/TestFramework_Prefix.pch7
-rw-r--r--tools/gyp/test/mac/framework/empty.c0
-rw-r--r--tools/gyp/test/mac/framework/framework.gyp74
-rwxr-xr-xtools/gyp/test/mac/gyptest-app.py44
-rwxr-xr-xtools/gyp/test/mac/gyptest-copies.py49
-rwxr-xr-xtools/gyp/test/mac/gyptest-debuginfo.py36
-rwxr-xr-xtools/gyp/test/mac/gyptest-framework.py50
-rwxr-xr-xtools/gyp/test/mac/gyptest-infoplist-process.py51
-rwxr-xr-xtools/gyp/test/mac/gyptest-loadable-module.py45
-rwxr-xr-xtools/gyp/test/mac/gyptest-postbuild-fail.py53
-rwxr-xr-xtools/gyp/test/mac/gyptest-postbuild.py51
-rwxr-xr-xtools/gyp/test/mac/gyptest-prefixheader.py19
-rwxr-xr-xtools/gyp/test/mac/gyptest-rebuild.py29
-rwxr-xr-xtools/gyp/test/mac/gyptest-strip.py54
-rwxr-xr-xtools/gyp/test/mac/gyptest-type-envvars.py24
-rwxr-xr-xtools/gyp/test/mac/gyptest-xcode-env-order.py30
-rw-r--r--tools/gyp/test/mac/infoplist-process/Info.plist36
-rw-r--r--tools/gyp/test/mac/infoplist-process/main.c7
-rw-r--r--tools/gyp/test/mac/infoplist-process/test1.gyp25
-rw-r--r--tools/gyp/test/mac/infoplist-process/test2.gyp25
-rw-r--r--tools/gyp/test/mac/infoplist-process/test3.gyp25
-rw-r--r--tools/gyp/test/mac/loadable-module/Info.plist26
-rw-r--r--tools/gyp/test/mac/loadable-module/module.c11
-rw-r--r--tools/gyp/test/mac/loadable-module/test.gyp18
-rw-r--r--tools/gyp/test/mac/postbuild-fail/file.c6
-rwxr-xr-xtools/gyp/test/mac/postbuild-fail/postbuild-fail.sh6
-rw-r--r--tools/gyp/test/mac/postbuild-fail/test.gyp38
-rwxr-xr-xtools/gyp/test/mac/postbuild-fail/touch-dynamic.sh7
-rwxr-xr-xtools/gyp/test/mac/postbuild-fail/touch-static.sh7
-rw-r--r--tools/gyp/test/mac/postbuilds/file.c4
-rwxr-xr-xtools/gyp/test/mac/postbuilds/script/shared_library_postbuild.sh23
-rwxr-xr-xtools/gyp/test/mac/postbuilds/script/static_library_postbuild.sh23
-rw-r--r--tools/gyp/test/mac/postbuilds/subdirectory/nested_target.gyp45
-rw-r--r--tools/gyp/test/mac/postbuilds/test.gyp79
-rw-r--r--tools/gyp/test/mac/prefixheader/file.c1
-rw-r--r--tools/gyp/test/mac/prefixheader/header.h1
-rw-r--r--tools/gyp/test/mac/prefixheader/test.gyp25
-rw-r--r--tools/gyp/test/mac/strip/file.c9
-rw-r--r--tools/gyp/test/mac/strip/strip.saves5
-rw-r--r--tools/gyp/test/mac/strip/test.gyp115
-rw-r--r--tools/gyp/test/mac/type_envvars/file.c6
-rw-r--r--tools/gyp/test/mac/type_envvars/test.gyp89
-rwxr-xr-xtools/gyp/test/mac/type_envvars/test_bundle_executable.sh9
-rwxr-xr-xtools/gyp/test/mac/type_envvars/test_bundle_loadable_module.sh9
-rwxr-xr-xtools/gyp/test/mac/type_envvars/test_bundle_shared_library.sh9
-rwxr-xr-xtools/gyp/test/mac/type_envvars/test_nonbundle_executable.sh10
-rwxr-xr-xtools/gyp/test/mac/type_envvars/test_nonbundle_loadable_module.sh9
-rwxr-xr-xtools/gyp/test/mac/type_envvars/test_nonbundle_shared_library.sh9
-rwxr-xr-xtools/gyp/test/mac/type_envvars/test_nonbundle_static_library.sh9
-rw-r--r--tools/gyp/test/mac/xcode-env-order/Info.plist38
-rw-r--r--tools/gyp/test/mac/xcode-env-order/main.c7
-rw-r--r--tools/gyp/test/mac/xcode-env-order/test.gyp23
-rw-r--r--tools/gyp/test/make/dependencies.gyp15
-rwxr-xr-xtools/gyp/test/make/gyptest-dependencies.py31
-rwxr-xr-xtools/gyp/test/make/gyptest-noload.py57
-rw-r--r--tools/gyp/test/make/main.cc12
-rw-r--r--tools/gyp/test/make/main.h0
-rw-r--r--tools/gyp/test/make/noload/all.gyp18
-rw-r--r--tools/gyp/test/make/noload/lib/shared.c3
-rw-r--r--tools/gyp/test/make/noload/lib/shared.gyp16
-rw-r--r--tools/gyp/test/make/noload/lib/shared.h1
-rw-r--r--tools/gyp/test/make/noload/main.c9
-rwxr-xr-xtools/gyp/test/module/gyptest-default.py28
-rw-r--r--tools/gyp/test/module/src/lib1.c10
-rw-r--r--tools/gyp/test/module/src/lib2.c10
-rw-r--r--tools/gyp/test/module/src/module.gyp55
-rw-r--r--tools/gyp/test/module/src/program.c111
-rw-r--r--tools/gyp/test/msvs/express/base/base.gyp22
-rw-r--r--tools/gyp/test/msvs/express/express.gyp19
-rwxr-xr-xtools/gyp/test/msvs/express/gyptest-express.py29
-rwxr-xr-xtools/gyp/test/msvs/precompiled/gyptest-all.py23
-rw-r--r--tools/gyp/test/msvs/precompiled/hello.c14
-rw-r--r--tools/gyp/test/msvs/precompiled/hello.gyp19
-rw-r--r--tools/gyp/test/msvs/precompiled/hello2.c13
-rw-r--r--tools/gyp/test/msvs/precompiled/precomp.c8
-rwxr-xr-xtools/gyp/test/multiple-targets/gyptest-all.py35
-rwxr-xr-xtools/gyp/test/multiple-targets/gyptest-default.py35
-rw-r--r--tools/gyp/test/multiple-targets/src/common.c7
-rw-r--r--tools/gyp/test/multiple-targets/src/multiple.gyp24
-rw-r--r--tools/gyp/test/multiple-targets/src/prog1.c10
-rw-r--r--tools/gyp/test/multiple-targets/src/prog2.c10
-rwxr-xr-xtools/gyp/test/ninja/action_dependencies/gyptest-action-dependencies.py45
-rw-r--r--tools/gyp/test/ninja/action_dependencies/src/a.c10
-rw-r--r--tools/gyp/test/ninja/action_dependencies/src/a.h13
-rw-r--r--tools/gyp/test/ninja/action_dependencies/src/action_dependencies.gyp88
-rw-r--r--tools/gyp/test/ninja/action_dependencies/src/b.c17
-rw-r--r--tools/gyp/test/ninja/action_dependencies/src/b.h13
-rw-r--r--tools/gyp/test/ninja/action_dependencies/src/c.c10
-rw-r--r--tools/gyp/test/ninja/action_dependencies/src/c.h13
-rwxr-xr-xtools/gyp/test/ninja/action_dependencies/src/emit.py11
-rwxr-xr-xtools/gyp/test/no-output/gyptest-no-output.py21
-rw-r--r--tools/gyp/test/no-output/src/nooutput.gyp17
-rwxr-xr-xtools/gyp/test/product/gyptest-product.py43
-rw-r--r--tools/gyp/test/product/hello.c15
-rw-r--r--tools/gyp/test/product/product.gyp128
-rw-r--r--tools/gyp/test/relative/foo/a/a.cc9
-rw-r--r--tools/gyp/test/relative/foo/a/a.gyp13
-rw-r--r--tools/gyp/test/relative/foo/a/c/c.cc9
-rw-r--r--tools/gyp/test/relative/foo/a/c/c.gyp12
-rw-r--r--tools/gyp/test/relative/foo/b/b.cc9
-rw-r--r--tools/gyp/test/relative/foo/b/b.gyp9
-rwxr-xr-xtools/gyp/test/relative/gyptest-default.py25
-rwxr-xr-xtools/gyp/test/rules-dirname/gyptest-dirname.py38
-rw-r--r--tools/gyp/test/rules-dirname/src/actions.gyp15
-rwxr-xr-xtools/gyp/test/rules-dirname/src/copy-file.py11
-rw-r--r--tools/gyp/test/rules-dirname/src/subdir/a/b/c.gencc11
-rw-r--r--tools/gyp/test/rules-dirname/src/subdir/a/b/c.printvars1
-rw-r--r--tools/gyp/test/rules-dirname/src/subdir/foo/bar/baz.gencc11
-rw-r--r--tools/gyp/test/rules-dirname/src/subdir/foo/bar/baz.printvars1
-rw-r--r--tools/gyp/test/rules-dirname/src/subdir/input-rule-dirname.gyp92
-rw-r--r--tools/gyp/test/rules-dirname/src/subdir/main.cc12
-rwxr-xr-xtools/gyp/test/rules-dirname/src/subdir/printvars.py14
-rwxr-xr-xtools/gyp/test/rules-rebuild/gyptest-all.py70
-rwxr-xr-xtools/gyp/test/rules-rebuild/gyptest-default.py70
-rw-r--r--tools/gyp/test/rules-rebuild/src/main.c12
-rwxr-xr-xtools/gyp/test/rules-rebuild/src/make-sources.py19
-rw-r--r--tools/gyp/test/rules-rebuild/src/prog1.in7
-rw-r--r--tools/gyp/test/rules-rebuild/src/prog2.in7
-rw-r--r--tools/gyp/test/rules-rebuild/src/same_target.gyp31
-rwxr-xr-xtools/gyp/test/rules-variables/gyptest-rules-variables.py26
-rw-r--r--tools/gyp/test/rules-variables/src/input_ext.c9
-rw-r--r--tools/gyp/test/rules-variables/src/input_name/test.c9
-rw-r--r--tools/gyp/test/rules-variables/src/input_path/subdir/test.c9
-rw-r--r--tools/gyp/test/rules-variables/src/subdir/input_dirname.c9
-rw-r--r--tools/gyp/test/rules-variables/src/subdir/test.c18
-rw-r--r--tools/gyp/test/rules-variables/src/test.input_root.c9
-rw-r--r--tools/gyp/test/rules-variables/src/variables.gyp30
-rwxr-xr-xtools/gyp/test/rules/gyptest-all.py66
-rwxr-xr-xtools/gyp/test/rules/gyptest-default.py55
-rwxr-xr-xtools/gyp/test/rules/gyptest-input-root.py26
-rw-r--r--tools/gyp/test/rules/src/actions.gyp21
-rwxr-xr-xtools/gyp/test/rules/src/copy-file.py11
-rw-r--r--tools/gyp/test/rules/src/external/external.gyp66
-rw-r--r--tools/gyp/test/rules/src/external/file1.in1
-rw-r--r--tools/gyp/test/rules/src/external/file2.in1
-rw-r--r--tools/gyp/test/rules/src/input-root.gyp24
-rwxr-xr-xtools/gyp/test/rules/src/rule.py17
-rw-r--r--tools/gyp/test/rules/src/somefile.ext0
-rw-r--r--tools/gyp/test/rules/src/subdir1/executable.gyp37
-rw-r--r--tools/gyp/test/rules/src/subdir1/function1.in6
-rw-r--r--tools/gyp/test/rules/src/subdir1/function2.in6
-rw-r--r--tools/gyp/test/rules/src/subdir1/program.c12
-rw-r--r--tools/gyp/test/rules/src/subdir2/file1.in1
-rw-r--r--tools/gyp/test/rules/src/subdir2/file2.in1
-rw-r--r--tools/gyp/test/rules/src/subdir2/never_used.gyp31
-rw-r--r--tools/gyp/test/rules/src/subdir2/no_inputs.gyp32
-rw-r--r--tools/gyp/test/rules/src/subdir2/none.gyp33
-rw-r--r--tools/gyp/test/rules/src/subdir3/executable2.gyp37
-rw-r--r--tools/gyp/test/rules/src/subdir3/function3.in6
-rw-r--r--tools/gyp/test/rules/src/subdir3/program.c10
-rw-r--r--tools/gyp/test/rules/src/subdir4/asm-function.asm10
-rw-r--r--tools/gyp/test/rules/src/subdir4/build-asm.gyp49
-rw-r--r--tools/gyp/test/rules/src/subdir4/program.c19
-rwxr-xr-xtools/gyp/test/same-gyp-name/gyptest-all.py34
-rwxr-xr-xtools/gyp/test/same-gyp-name/gyptest-default.py34
-rw-r--r--tools/gyp/test/same-gyp-name/src/all.gyp16
-rw-r--r--tools/gyp/test/same-gyp-name/src/subdir1/executable.gyp15
-rw-r--r--tools/gyp/test/same-gyp-name/src/subdir1/main1.cc6
-rw-r--r--tools/gyp/test/same-gyp-name/src/subdir2/executable.gyp15
-rw-r--r--tools/gyp/test/same-gyp-name/src/subdir2/main2.cc6
-rwxr-xr-xtools/gyp/test/same-name/gyptest-all.py34
-rwxr-xr-xtools/gyp/test/same-name/gyptest-default.py34
-rw-r--r--tools/gyp/test/same-name/src/all.gyp38
-rw-r--r--tools/gyp/test/same-name/src/func.c6
-rw-r--r--tools/gyp/test/same-name/src/prog1.c16
-rw-r--r--tools/gyp/test/same-name/src/prog2.c16
-rw-r--r--tools/gyp/test/same-name/src/subdir1/func.c6
-rw-r--r--tools/gyp/test/same-name/src/subdir2/func.c6
-rwxr-xr-xtools/gyp/test/same-target-name/gyptest-same-target-name.py18
-rw-r--r--tools/gyp/test/same-target-name/src/all.gyp16
-rw-r--r--tools/gyp/test/same-target-name/src/executable1.gyp15
-rw-r--r--tools/gyp/test/same-target-name/src/executable2.gyp15
-rwxr-xr-xtools/gyp/test/scons_tools/gyptest-tools.py26
-rw-r--r--tools/gyp/test/scons_tools/site_scons/site_tools/this_tool.py10
-rw-r--r--tools/gyp/test/scons_tools/tools.c13
-rw-r--r--tools/gyp/test/scons_tools/tools.gyp18
-rwxr-xr-xtools/gyp/test/settings/gyptest-settings.py18
-rw-r--r--tools/gyp/test/settings/settings.gyp20
-rwxr-xr-xtools/gyp/test/sibling/gyptest-all.py39
-rwxr-xr-xtools/gyp/test/sibling/gyptest-relocate.py41
-rw-r--r--tools/gyp/test/sibling/src/build/all.gyp17
-rw-r--r--tools/gyp/test/sibling/src/prog1/prog1.c7
-rw-r--r--tools/gyp/test/sibling/src/prog1/prog1.gyp15
-rw-r--r--tools/gyp/test/sibling/src/prog2/prog2.c7
-rw-r--r--tools/gyp/test/sibling/src/prog2/prog2.gyp15
-rwxr-xr-xtools/gyp/test/small/gyptest-small.py51
-rwxr-xr-xtools/gyp/test/subdirectory/gyptest-SYMROOT-all.py36
-rwxr-xr-xtools/gyp/test/subdirectory/gyptest-SYMROOT-default.py37
-rwxr-xr-xtools/gyp/test/subdirectory/gyptest-subdir-all.py34
-rwxr-xr-xtools/gyp/test/subdirectory/gyptest-subdir-default.py33
-rwxr-xr-xtools/gyp/test/subdirectory/gyptest-subdir2-deep.py25
-rwxr-xr-xtools/gyp/test/subdirectory/gyptest-top-all.py43
-rwxr-xr-xtools/gyp/test/subdirectory/gyptest-top-default.py43
-rw-r--r--tools/gyp/test/subdirectory/src/prog1.c7
-rw-r--r--tools/gyp/test/subdirectory/src/prog1.gyp21
-rw-r--r--tools/gyp/test/subdirectory/src/subdir/prog2.c7
-rw-r--r--tools/gyp/test/subdirectory/src/subdir/prog2.gyp18
-rw-r--r--tools/gyp/test/subdirectory/src/subdir/subdir2/prog3.c7
-rw-r--r--tools/gyp/test/subdirectory/src/subdir/subdir2/prog3.gyp18
-rw-r--r--tools/gyp/test/subdirectory/src/symroot.gypi16
-rwxr-xr-xtools/gyp/test/toolsets/gyptest-toolsets.py23
-rw-r--r--tools/gyp/test/toolsets/main.cc11
-rw-r--r--tools/gyp/test/toolsets/toolsets.cc11
-rw-r--r--tools/gyp/test/toolsets/toolsets.gyp38
-rwxr-xr-xtools/gyp/test/toplevel-dir/gyptest-toplevel-dir.py31
-rw-r--r--tools/gyp/test/toplevel-dir/src/sub1/main.gyp18
-rw-r--r--tools/gyp/test/toplevel-dir/src/sub1/prog1.c7
-rw-r--r--tools/gyp/test/toplevel-dir/src/sub2/prog2.c7
-rw-r--r--tools/gyp/test/toplevel-dir/src/sub2/prog2.gyp15
-rw-r--r--tools/gyp/test/variables/commands/commands-repeated.gyp128
-rw-r--r--tools/gyp/test/variables/commands/commands-repeated.gyp.stdout405
-rw-r--r--tools/gyp/test/variables/commands/commands-repeated.gypd.golden72
-rw-r--r--tools/gyp/test/variables/commands/commands.gyp84
-rw-r--r--tools/gyp/test/variables/commands/commands.gyp.ignore-env.stdout254
-rw-r--r--tools/gyp/test/variables/commands/commands.gyp.stdout254
-rw-r--r--tools/gyp/test/variables/commands/commands.gypd.golden54
-rw-r--r--tools/gyp/test/variables/commands/commands.gypi16
-rwxr-xr-xtools/gyp/test/variables/commands/gyptest-commands-ignore-env.py46
-rwxr-xr-xtools/gyp/test/variables/commands/gyptest-commands-repeated.py40
-rwxr-xr-xtools/gyp/test/variables/commands/gyptest-commands.py39
-rwxr-xr-xtools/gyp/test/variables/commands/update_golden11
-rw-r--r--tools/gyp/test/variables/filelist/filelist.gyp.stdout174
-rw-r--r--tools/gyp/test/variables/filelist/filelist.gypd.golden43
-rwxr-xr-xtools/gyp/test/variables/filelist/gyptest-filelist.py50
-rw-r--r--tools/gyp/test/variables/filelist/src/filelist.gyp93
-rwxr-xr-xtools/gyp/test/variables/filelist/update_golden8
-rwxr-xr-xtools/gyp/test/variants/gyptest-variants.py45
-rw-r--r--tools/gyp/test/variants/src/variants.c13
-rw-r--r--tools/gyp/test/variants/src/variants.gyp27
-rwxr-xr-xtools/gyp/tools/graphviz.py11
-rwxr-xr-x[-rw-r--r--]tools/gyp/tools/pretty_gyp.py296
-rwxr-xr-xtools/gyp/tools/pretty_sln.py9
-rwxr-xr-xtools/gyp/tools/pretty_vcproj.py28
-rwxr-xr-xtools/gyp_addon27
-rwxr-xr-xtools/gyp_node5
-rw-r--r--tools/installer.js120
-rw-r--r--tools/scons/scons-LICENSE25
-rw-r--r--tools/scons/scons-README204
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Action.py1147
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Builder.py844
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/CacheDir.py217
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Conftest.py778
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Debug.py216
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Defaults.py463
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Environment.py2300
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Errors.py198
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Executor.py393
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Job.py429
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Memoize.py286
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Node/Alias.py147
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Node/FS.py3075
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Node/Python.py119
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Node/__init__.py1330
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Options/BoolOption.py44
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Options/EnumOption.py44
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Options/ListOption.py44
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Options/PackageOption.py44
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Options/PathOption.py70
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Options/__init__.py68
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/PathList.py226
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Platform/__init__.py216
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Platform/aix.py65
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Platform/cygwin.py49
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Platform/darwin.py40
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Platform/hpux.py40
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Platform/irix.py38
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Platform/os2.py49
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Platform/posix.py258
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Platform/sunos.py44
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Platform/win32.py324
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/SConf.py1012
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/SConsign.py375
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Scanner/C.py126
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Scanner/D.py68
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Scanner/Dir.py105
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Scanner/Fortran.py314
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Scanner/IDL.py42
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Scanner/LaTeX.py334
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Scanner/Prog.py97
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Scanner/RC.py49
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Scanner/__init__.py406
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Script/Interactive.py376
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Script/Main.py1321
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Script/SConsOptions.py940
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Script/SConscript.py632
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Script/__init__.py408
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Sig.py57
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Subst.py884
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Taskmaster.py985
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/386asm.py55
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/BitKeeper.py59
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/CVS.py67
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/FortranCommon.py241
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/JavaCommon.py317
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/Perforce.py98
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/PharLapCommon.py132
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/RCS.py58
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/SCCS.py58
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/Subversion.py65
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/__init__.py667
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/aixc++.py76
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/aixcc.py68
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/aixf77.py74
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/aixlink.py70
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/applelink.py65
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/ar.py57
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/as.py72
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/bcc32.py76
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/c++.py93
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/cc.py108
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/cvf.py52
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/default.py44
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/dmd.py218
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/dvi.py58
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/dvipdf.py119
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/dvips.py88
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/f77.py56
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/f90.py56
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/f95.py57
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/filesystem.py92
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/fortran.py57
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/g++.py84
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/g77.py67
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/gas.py47
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/gcc.py74
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/gfortran.py58
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/gnulink.py57
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/gs.py75
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/hpc++.py79
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/hpcc.py47
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/hplink.py71
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/icc.py53
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/icl.py46
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/ifl.py66
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/ifort.py83
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/ilink.py53
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/ilink32.py54
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/install.py223
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/intelc.py482
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/jar.py104
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/javac.py228
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/javah.py132
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/latex.py76
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/lex.py93
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/link.py112
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/linkloc.py105
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/m4.py57
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/masm.py71
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/midl.py90
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/mingw.py151
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/mslib.py76
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/mslink.py249
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/msvc.py766
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/msvs.py1815
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/mwcc.py202
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/mwld.py101
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/nasm.py66
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/packaging/__init__.py306
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/packaging/ipk.py179
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/packaging/msi.py521
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/packaging/rpm.py362
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/packaging/src_tarbz2.py37
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/packaging/src_targz.py37
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/packaging/src_zip.py37
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/packaging/tarbz2.py38
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/packaging/targz.py38
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/packaging/zip.py38
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/pdf.py72
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/pdflatex.py75
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/pdftex.py99
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/qt.py330
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/rmic.py115
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/rpcgen.py64
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/rpm.py126
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/sgiar.py62
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/sgic++.py52
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/sgicc.py47
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/sgilink.py57
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/sunar.py61
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/sunc++.py100
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/suncc.py52
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/sunf77.py57
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/sunf90.py58
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/sunf95.py58
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/sunlink.py71
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/swig.py118
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/tar.py67
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/tex.py661
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/tlib.py47
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/wix.py94
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/yacc.py125
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Tool/zip.py94
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Util.py1577
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Variables/BoolVariable.py85
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Variables/EnumVariable.py101
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Variables/ListVariable.py133
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Variables/PackageVariable.py103
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Variables/PathVariable.py141
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Variables/__init__.py304
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/Warnings.py193
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/__init__.py43
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/compat/__init__.py244
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/compat/_scons_UserString.py92
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/compat/_scons_hashlib.py85
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/compat/_scons_itertools.py118
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/compat/_scons_optparse.py1719
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/compat/_scons_sets.py577
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/compat/_scons_sets15.py170
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/compat/_scons_shlex.py319
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/compat/_scons_subprocess.py1290
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/compat/_scons_textwrap.py376
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/compat/builtins.py181
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/cpp.py592
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/dblite.py219
-rw-r--r--tools/scons/scons-local-1.2.0/SCons/exitfuncs.py71
-rwxr-xr-xtools/scons/scons-time.py1513
-rwxr-xr-xtools/scons/scons.py165
-rwxr-xr-xtools/scons/sconsign.py502
-rw-r--r--tools/wafadmin/Tools/node_addon.py13
-rw-r--r--vcbuild.bat4
-rw-r--r--wscript1045
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
diff --git a/AUTHORS b/AUTHORS
index 3afd5f597b..e5c82011f0 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -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>
diff --git a/ChangeLog b/ChangeLog
index f4b5fc56f6..b5306ae9cc 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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)
diff --git a/Makefile b/Makefile
index 1609495749..df1cf7b8d2 100644
--- a/Makefile
+++ b/Makefile
@@ -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': [
diff --git a/configure b/configure
index ca01021d05..d370f166c5 100755
--- a/configure
+++ b/configure
@@ -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 = &in;
+
+ 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, &not_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(&not_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, &not_empty_array);
+ __ Drop(1); // Adjust stack.
+ __ mov(r0, Operand(0)); // Treat this as a call with argc of zero.
+ __ b(&empty_array);
+
+ __ bind(&not_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, &not_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, &not_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(&not_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, &copy_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(&copy_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, &notin, &slow);
__ str(r0, mapped_location);
__ add(r6, r3, r5);
- __ RecordWrite(r3, r6, r9);
+ __ mov(r9, r0);
+ __ RecordWrite(r3, r6, r9, kLRHasNotBeenSaved, kDontSaveFPRegs);
__ Ret();
__ bind(&notin);
// 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, &not_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(&not_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, &not_in_new_space);
- Abort("new-space object passed to RecordWriteHelper");
- bind(&not_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, &not_fast_object);
+ __ jmp(&fast_object);
+ // In case of fast smi-only, convert to fast object, otherwise bail out.
+ __ bind(&not_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", &currency)) {
- 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, &not_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(&copy);
__ inc(edi);
__ push(Operand(eax, 0));
- __ sub(Operand(eax), Immediate(kPointerSize));
- __ cmp(edi, Operand(ebx));
+ __ sub(eax, Immediate(kPointerSize));
+ __ cmp(edi, ebx);
__ j(less, &copy);
__ 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(&copy);
__ 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, &copy);
// 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, &not_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, &not_int32);
__ j(carry, &not_int32);
@@ -1548,9 +1769,9 @@ void BinaryOpStub::GenerateInt32Stub(MacroAssembler* masm) {
FloatingPointHelper::CheckLoadedIntegersWereInt32(masm, use_sse3_,
&not_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_,
&not_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, &not_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(&not_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, &not_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(&not_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(&parameters_test, Label::kNear);
__ bind(&parameters_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(&parameters_test);
- __ test(eax, Operand(eax));
+ __ test(eax, eax);
__ j(not_zero, &parameters_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, &not_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, &not_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, &not_outermost_js_2);
__ mov(Operand::StaticVariable(js_entry_sp), Immediate(0));
__ bind(&not_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, &not_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(&not_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, &not_original_string, Label::kNear);
+ Counters* counters = masm->isolate()->counters();
+ __ IncrementCounter(counters->sub_string_native(), 1);
+ __ ret(3 * kPointerSize);
+ __ bind(&not_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(&copy_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, &copy_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(&copy_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, &not_same, Label::kNear);
STATIC_ASSERT(EQUAL == 0);
STATIC_ASSERT(kSmiTag == 0);
@@ -6046,13 +6520,13 @@ void StringCompareStub::Generate(MacroAssembler* masm) {
__ bind(&not_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, &not_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, &regop, &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(&notin);
// 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, &not_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(&not_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, &not_in_new_space);
- Abort("new-space object passed to RecordWriteHelper");
- bind(&not_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(&not_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, &not_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, &not_heap_number, Label::kNear);
+ mov(length, Immediate(HeapNumber::kSize));
+ jmp(&is_data_object, Label::kNear);
+
+ bind(&not_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, &not_external, Label::kNear);
+ mov(length, Immediate(ExternalString::kSize));
+ jmp(&is_data_object, Label::kNear);
+
+ bind(&not_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, &not_fast_object, Label::kNear);
+ __ jmp(&fast_object);
+ // In case of fast smi-only, convert to fast object, otherwise bail out.
+ __ bind(&not_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(&not_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, &not_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 &regexp_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(&not_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(&not_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(&not_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(&not_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(&not_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(&not_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(&not_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(&not_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,
+ &not_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(&not_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(&copy_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(&copy_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, &notin, &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(&notin);
// 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(&not_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(&not_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(&not_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(&not_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(&not_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(&not_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(&not_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(&not_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, &not_in_new_space);
- Abort("new-space object passed to RecordWriteHelper");
- bind(&not_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, &not_fast_object);
+ __ jmp(&fast_object);
+ // In case of fast smi-only, convert to fast object, otherwise bail out.
+ __ bind(&not_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 &empty;
- }
-
- 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**>(&current->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, &parameters_);
- 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, &parameters_);
- 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, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#039;");
-};
+}
// 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, &not_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(&not_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, &not_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(&not_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, &not_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(&not_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, &not_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(&not_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, &not_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(&not_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, &not_original_string, Label::kNear);
+ Counters* counters = masm->isolate()->counters();
+ __ IncrementCounter(counters->sub_string_native(), 1);
+ __ ret(kArgumentsSize);
+ __ bind(&not_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(&copy_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, &copy_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(&copy_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, &regop, &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, &regop, &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(&notin);
// 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, &not_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(&not_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, &not_in_new_space, Label::kNear);
- Abort("new-space object passed to RecordWriteHelper");
- bind(&not_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(&not_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, &not_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, &not_heap_number, Label::kNear);
+ movq(length, Immediate(HeapNumber::kSize));
+ jmp(&is_data_object, Label::kNear);
+
+ bind(&not_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, &not_external, Label::kNear);
+ movq(length, Immediate(ExternalString::kSize));
+ jmp(&is_data_object, Label::kNear);
+
+ bind(&not_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, &not_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, &not_fast_object, Label::kNear);
+ __ jmp(&fast_object);
+ // In case of fast smi-only, convert to fast object, otherwise bail out.
+ __ bind(&not_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(&not_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, &not_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
deleted file mode 100644
index 1ed163ee1a..0000000000
--- a/doc/api_assets/anchor.png
+++ /dev/null
Binary files differ
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
deleted file mode 100644
index e6d9c2390e..0000000000
--- a/doc/footer-logo-alt.png
+++ /dev/null
Binary files differ
diff --git a/doc/icons-interior.png b/doc/icons-interior.png
deleted file mode 100644
index 6b3d483dc0..0000000000
--- a/doc/icons-interior.png
+++ /dev/null
Binary files differ
diff --git a/doc/anchor.png b/doc/images/anchor.png
index 1ed163ee1a..1ed163ee1a 100644
--- a/doc/anchor.png
+++ b/doc/images/anchor.png
Binary files differ
diff --git a/doc/close-downloads.png b/doc/images/close-downloads.png
index ba72523f7b..ba72523f7b 100644
--- a/doc/close-downloads.png
+++ b/doc/images/close-downloads.png
Binary files differ
diff --git a/doc/community-icons.png b/doc/images/community-icons.png
index b3d58f0f1e..b3d58f0f1e 100644
--- a/doc/community-icons.png
+++ b/doc/images/community-icons.png
Binary files differ
diff --git a/doc/download-logo.png b/doc/images/download-logo.png
index 253235f4c3..253235f4c3 100644
--- a/doc/download-logo.png
+++ b/doc/images/download-logo.png
Binary files differ
diff --git a/doc/ebay-logo.png b/doc/images/ebay-logo.png
index 8a51935dd9..8a51935dd9 100644
--- a/doc/ebay-logo.png
+++ b/doc/images/ebay-logo.png
Binary files differ
diff --git a/doc/api_assets/footer-logo-alt.png b/doc/images/footer-logo-alt.png
index e6d9c2390e..e6d9c2390e 100644
--- a/doc/api_assets/footer-logo-alt.png
+++ b/doc/images/footer-logo-alt.png
Binary files differ
diff --git a/doc/footer-logo.png b/doc/images/footer-logo.png
index 19a4eaadcb..19a4eaadcb 100644
--- a/doc/footer-logo.png
+++ b/doc/images/footer-logo.png
Binary files differ
diff --git a/doc/home-icons.png b/doc/images/home-icons.png
index 421dfdaf11..421dfdaf11 100644
--- a/doc/home-icons.png
+++ b/doc/images/home-icons.png
Binary files differ
diff --git a/doc/api_assets/icons-interior.png b/doc/images/icons-interior.png
index 6b3d483dc0..6b3d483dc0 100644
--- a/doc/api_assets/icons-interior.png
+++ b/doc/images/icons-interior.png
Binary files differ
diff --git a/doc/icons.png b/doc/images/icons.png
index 421dfdaf11..421dfdaf11 100644
--- a/doc/icons.png
+++ b/doc/images/icons.png
Binary files differ
diff --git a/doc/joyent-logo_orange_nodeorg-01.png b/doc/images/joyent-logo_orange_nodeorg-01.png
index 1b977395b8..1b977395b8 100644
--- a/doc/joyent-logo_orange_nodeorg-01.png
+++ b/doc/images/joyent-logo_orange_nodeorg-01.png
Binary files differ
diff --git a/doc/linkedin-logo.png b/doc/images/linkedin-logo.png
index 2ad9fb2c0c..2ad9fb2c0c 100644
--- a/doc/linkedin-logo.png
+++ b/doc/images/linkedin-logo.png
Binary files differ
diff --git a/doc/api_assets/logo-light.png b/doc/images/logo-light.png
index 898fcecc96..898fcecc96 100644
--- a/doc/api_assets/logo-light.png
+++ b/doc/images/logo-light.png
Binary files differ
diff --git a/doc/logo.png b/doc/images/logo.png
index ef3494efa8..ef3494efa8 100644
--- a/doc/logo.png
+++ b/doc/images/logo.png
Binary files differ
diff --git a/doc/logos/monitor.png b/doc/images/logos/monitor.png
index 18c804e2df..18c804e2df 100644
--- a/doc/logos/monitor.png
+++ b/doc/images/logos/monitor.png
Binary files differ
diff --git a/doc/logos/node-favicon.png b/doc/images/logos/node-favicon.png
index 3802fced9d..3802fced9d 100644
--- a/doc/logos/node-favicon.png
+++ b/doc/images/logos/node-favicon.png
Binary files differ
diff --git a/doc/logos/nodejs-1024x768.png b/doc/images/logos/nodejs-1024x768.png
index e571c091d2..e571c091d2 100644
--- a/doc/logos/nodejs-1024x768.png
+++ b/doc/images/logos/nodejs-1024x768.png
Binary files differ
diff --git a/doc/logos/nodejs-1280x1024.png b/doc/images/logos/nodejs-1280x1024.png
index 00f65d0de5..00f65d0de5 100644
--- a/doc/logos/nodejs-1280x1024.png
+++ b/doc/images/logos/nodejs-1280x1024.png
Binary files differ
diff --git a/doc/logos/nodejs-1440x900.png b/doc/images/logos/nodejs-1440x900.png
index 4b818cc682..4b818cc682 100644
--- a/doc/logos/nodejs-1440x900.png
+++ b/doc/images/logos/nodejs-1440x900.png
Binary files differ
diff --git a/doc/logos/nodejs-1920x1200.png b/doc/images/logos/nodejs-1920x1200.png
index 6b31fecf49..6b31fecf49 100644
--- a/doc/logos/nodejs-1920x1200.png
+++ b/doc/images/logos/nodejs-1920x1200.png
Binary files differ
diff --git a/doc/logos/nodejs-2560x1440.png b/doc/images/logos/nodejs-2560x1440.png
index ba46b7704e..ba46b7704e 100644
--- a/doc/logos/nodejs-2560x1440.png
+++ b/doc/images/logos/nodejs-2560x1440.png
Binary files differ
diff --git a/doc/logos/nodejs-black.eps b/doc/images/logos/nodejs-black.eps
index e3186994f5..e3186994f5 100644
--- a/doc/logos/nodejs-black.eps
+++ b/doc/images/logos/nodejs-black.eps
Binary files differ
diff --git a/doc/logos/nodejs-black.png b/doc/images/logos/nodejs-black.png
index a7c6d63db4..a7c6d63db4 100644
--- a/doc/logos/nodejs-black.png
+++ b/doc/images/logos/nodejs-black.png
Binary files differ
diff --git a/doc/logos/nodejs-dark.eps b/doc/images/logos/nodejs-dark.eps
index 11c230db15..11c230db15 100644
--- a/doc/logos/nodejs-dark.eps
+++ b/doc/images/logos/nodejs-dark.eps
Binary files differ
diff --git a/doc/logos/nodejs-dark.png b/doc/images/logos/nodejs-dark.png
index 56094a9b41..56094a9b41 100644
--- a/doc/logos/nodejs-dark.png
+++ b/doc/images/logos/nodejs-dark.png
Binary files differ
diff --git a/doc/logos/nodejs-green.eps b/doc/images/logos/nodejs-green.eps
index 362ac37d89..362ac37d89 100644
--- a/doc/logos/nodejs-green.eps
+++ b/doc/images/logos/nodejs-green.eps
Binary files differ
diff --git a/doc/logos/nodejs-green.png b/doc/images/logos/nodejs-green.png
index ee8a2fb550..ee8a2fb550 100644
--- a/doc/logos/nodejs-green.png
+++ b/doc/images/logos/nodejs-green.png
Binary files differ
diff --git a/doc/logos/nodejs-light.eps b/doc/images/logos/nodejs-light.eps
index 2317313e4f..2317313e4f 100644
--- a/doc/logos/nodejs-light.eps
+++ b/doc/images/logos/nodejs-light.eps
Binary files differ
diff --git a/doc/logos/nodejs.png b/doc/images/logos/nodejs.png
index df49579bb5..df49579bb5 100644
--- a/doc/logos/nodejs.png
+++ b/doc/images/logos/nodejs.png
Binary files differ
diff --git a/doc/microsoft-logo.png b/doc/images/microsoft-logo.png
index 6dac0f39b6..6dac0f39b6 100644
--- a/doc/microsoft-logo.png
+++ b/doc/images/microsoft-logo.png
Binary files differ
diff --git a/doc/community/not-invented-here.png b/doc/images/not-invented-here.png
index 5d4efa0532..5d4efa0532 100644
--- a/doc/community/not-invented-here.png
+++ b/doc/images/not-invented-here.png
Binary files differ
diff --git a/doc/api_assets/platform-icons.png b/doc/images/platform-icons.png
index d6624a5a64..d6624a5a64 100644
--- a/doc/api_assets/platform-icons.png
+++ b/doc/images/platform-icons.png
Binary files differ
diff --git a/doc/ryan-speaker.jpg b/doc/images/ryan-speaker.jpg
index c0f6263441..c0f6263441 100644
--- a/doc/ryan-speaker.jpg
+++ b/doc/images/ryan-speaker.jpg
Binary files differ
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
index e7564e169d..e7564e169d 100644
--- a/doc/sponsored.png
+++ b/doc/images/sponsored.png
Binary files differ
diff --git a/doc/api_assets/twitter-bird.png b/doc/images/twitter-bird.png
index b619b2a0d3..b619b2a0d3 100644
--- a/doc/api_assets/twitter-bird.png
+++ b/doc/images/twitter-bird.png
Binary files differ
diff --git a/doc/yahoo-logo.png b/doc/images/yahoo-logo.png
index 05d75b3513..05d75b3513 100644
--- a/doc/yahoo-logo.png
+++ b/doc/images/yahoo-logo.png
Binary files differ
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
deleted file mode 100644
index 898fcecc96..0000000000
--- a/doc/logo-light.png
+++ /dev/null
Binary files differ
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
deleted file mode 100644
index d6624a5a64..0000000000
--- a/doc/platform-icons.png
+++ /dev/null
Binary files differ
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 &amp; Documentation</title>
+ <title>{{section}}Node.js v0.7.4 Manual &amp; 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 &amp; Documentation</h1>
+ <h1>Node.js v0.7.4 Manual &amp; 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
deleted file mode 100644
index b619b2a0d3..0000000000
--- a/doc/twitter-bird.png
+++ /dev/null
Binary files differ
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);
});
+
};
diff --git a/lib/fs.js b/lib/fs.js
index 3603232abf..09a3dcb8ae 100644
--- a/lib/fs.js
+++ b/lib/fs.js
@@ -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) {}
+}
diff --git a/lib/os.js b/lib/os.js
index f226e3ecbf..21a95a1359 100644
--- a/lib/os.js
+++ b/lib/os.js
@@ -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);
diff --git a/node.gyp b/node.gyp
index 766788bd6e..24f579e054 100644
--- a/node.gyp
+++ b/node.gyp
@@ -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, &quot; `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, &quot; `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, "&", "&amp;") # do this first
- s = string.replace(s, "'", "&apos;")
- s = string.replace(s, '"', "&quot;")
- 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')